{"id":15,"date":"2009-01-18T17:06:50","date_gmt":"2009-01-19T00:06:50","guid":{"rendered":"http:\/\/www.connorgarvey.com\/blog\/?p=15"},"modified":"2010-02-12T09:45:54","modified_gmt":"2010-02-12T16:45:54","slug":"creating-soft-keys-that-work-with-text-watchers-in-android","status":"publish","type":"post","link":"https:\/\/connorgarvey.com\/blog\/?p=15","title":{"rendered":"Creating soft-keys that work with text watchers in Android"},"content":{"rendered":"<p>Recently, I created a screen with soft keys that could be used for number input.\u00a0 <strong>Scroll down for some source.<\/strong> Everything was working perfectly.\u00a0 Each number key called field.append(&#8220;1&#8221;), passing in the correct number.\u00a0 Then, a text watcher would be notified of the change and reformat the field.\u00a0 The problem arose when I wrote the backspace method.\u00a0 TextView doesn&#8217;t have a convenience method for removing characters from a field, so I used setText, passing in the number with the last character removed.\u00a0 Unfortunately, when I did that, the text wacher wasn&#8217;t notified of the change!\u00a0 Ugh&#8230;\u00a0 Android is very well designed, but some implementation details are really not written well.\u00a0 I decided it was time to look in the Android source.\u00a0 If the append method caused a notification to the text watcher, I would write a method that worked the same way.<\/p>\n<h2><span style=\"color: #000000;\">Supporting number keys<\/span><\/h2>\n<p>This was really simple.<\/p>\n<div id=\"wpshdo_1\" class=\"wp-synhighlighter-outer\"><div id=\"wpshdt_1\" class=\"wp-synhighlighter-expanded\"><table border=\"0\" width=\"100%\"><tr><td align=\"left\" width=\"80%\"><a name=\"#codesyntax_1\"><\/a><a id=\"wpshat_1\" class=\"wp-synhighlighter-title\" href=\"#codesyntax_1\"  onClick=\"javascript:wpsh_toggleBlock(1)\" title=\"Click to show\/hide code block\">Code block<\/a><\/td><td align=\"right\"><a href=\"#codesyntax_1\" onClick=\"javascript:wpsh_code(1)\" title=\"Show code only\"><img decoding=\"async\" border=\"0\" style=\"border: 0 none\" src=\"https:\/\/connorgarvey.com\/blog\/wp-content\/plugins\/wp-synhighlight\/themes\/default\/images\/code.png\" \/><\/a>&nbsp;<a href=\"#codesyntax_1\" onClick=\"javascript:wpsh_print(1)\" title=\"Print code\"><img decoding=\"async\" border=\"0\" style=\"border: 0 none\" src=\"https:\/\/connorgarvey.com\/blog\/wp-content\/plugins\/wp-synhighlight\/themes\/default\/images\/printer.png\" \/><\/a>&nbsp;<a href=\"https:\/\/connorgarvey.com\/blog\/wp-content\/plugins\/wp-synhighlight\/About.html\" target=\"_blank\" title=\"Show plugin information\"><img decoding=\"async\" border=\"0\" style=\"border: 0 none\" src=\"https:\/\/connorgarvey.com\/blog\/wp-content\/plugins\/wp-synhighlight\/themes\/default\/images\/info.gif\" \/><\/a>&nbsp;<\/td><\/tr><\/table><\/div><div id=\"wpshdi_1\" class=\"wp-synhighlighter-inner\" style=\"display: block;\"><pre class=\"java\" style=\"font-family:monospace;\"><span class=\"kw1\">private<\/span> <span class=\"kw1\">class<\/span> CharacterButtonListener <span class=\"kw1\">implements<\/span>\n      <a href=\"http:\/\/www.google.com\/search?hl=en&amp;q=allinurl%3Aview+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky\"><span class=\"kw3\">View<\/span><\/a>.<span class=\"me1\">OnClickListener<\/span> <span class=\"br0\">&#123;<\/span>\n    <span class=\"kw1\">private<\/span> TextView field<span class=\"sy0\">;<\/span>\n    <span class=\"kw1\">private<\/span> <a href=\"http:\/\/www.google.com\/search?hl=en&amp;q=allinurl%3Astring+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky\"><span class=\"kw3\">String<\/span><\/a> character<span class=\"sy0\">;<\/span>\n&nbsp;\n    <span class=\"kw1\">public<\/span> CharacterButtonListener<span class=\"br0\">&#40;<\/span>TextView field,\n        <a href=\"http:\/\/www.google.com\/search?hl=en&amp;q=allinurl%3Astring+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky\"><span class=\"kw3\">String<\/span><\/a> character<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span>\n      <span class=\"kw1\">this<\/span>.<span class=\"me1\">field<\/span> <span class=\"sy0\">=<\/span> field<span class=\"sy0\">;<\/span>\n      <span class=\"kw1\">this<\/span>.<span class=\"me1\">character<\/span> <span class=\"sy0\">=<\/span> character<span class=\"sy0\">;<\/span>\n    <span class=\"br0\">&#125;<\/span>\n&nbsp;\n    @Override\n    <span class=\"kw1\">public<\/span> <span class=\"kw4\">void<\/span> onClick<span class=\"br0\">&#40;<\/span><a href=\"http:\/\/www.google.com\/search?hl=en&amp;q=allinurl%3Aview+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky\"><span class=\"kw3\">View<\/span><\/a> v<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span>\n      <span class=\"kw1\">this<\/span>.<span class=\"me1\">field<\/span>.<span class=\"me1\">append<\/span><span class=\"br0\">&#40;<\/span><span class=\"kw1\">this<\/span>.<span class=\"me1\">character<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span>\n    <span class=\"br0\">&#125;<\/span>\n  <span class=\"br0\">&#125;<\/span><\/pre><\/div><\/div>\n<h2><span style=\"color: #000000;\">Supporting the backspace key<\/span><\/h2>\n<p>Since the setText method wasn&#8217;t notifying the TextWatcher, I looked into Android&#8217;s source to find this solution.\u00a0 It&#8217;s ugly, but it works.<\/p>\n<div id=\"wpshdo_2\" class=\"wp-synhighlighter-outer\"><div id=\"wpshdt_2\" class=\"wp-synhighlighter-expanded\"><table border=\"0\" width=\"100%\"><tr><td align=\"left\" width=\"80%\"><a name=\"#codesyntax_2\"><\/a><a id=\"wpshat_2\" class=\"wp-synhighlighter-title\" href=\"#codesyntax_2\"  onClick=\"javascript:wpsh_toggleBlock(2)\" title=\"Click to show\/hide code block\">Code block<\/a><\/td><td align=\"right\"><a href=\"#codesyntax_2\" onClick=\"javascript:wpsh_code(2)\" title=\"Show code only\"><img decoding=\"async\" border=\"0\" style=\"border: 0 none\" src=\"https:\/\/connorgarvey.com\/blog\/wp-content\/plugins\/wp-synhighlight\/themes\/default\/images\/code.png\" \/><\/a>&nbsp;<a href=\"#codesyntax_2\" onClick=\"javascript:wpsh_print(2)\" title=\"Print code\"><img decoding=\"async\" border=\"0\" style=\"border: 0 none\" src=\"https:\/\/connorgarvey.com\/blog\/wp-content\/plugins\/wp-synhighlight\/themes\/default\/images\/printer.png\" \/><\/a>&nbsp;<a href=\"https:\/\/connorgarvey.com\/blog\/wp-content\/plugins\/wp-synhighlight\/About.html\" target=\"_blank\" title=\"Show plugin information\"><img decoding=\"async\" border=\"0\" style=\"border: 0 none\" src=\"https:\/\/connorgarvey.com\/blog\/wp-content\/plugins\/wp-synhighlight\/themes\/default\/images\/info.gif\" \/><\/a>&nbsp;<\/td><\/tr><\/table><\/div><div id=\"wpshdi_2\" class=\"wp-synhighlighter-inner\" style=\"display: block;\"><pre class=\"java\" style=\"font-family:monospace;\"><span class=\"kw1\">this<\/span>.<span class=\"me1\">buttonBack<\/span>.<span class=\"me1\">setOnClickListener<\/span><span class=\"br0\">&#40;<\/span><span class=\"kw1\">new<\/span> <a href=\"http:\/\/www.google.com\/search?hl=en&amp;q=allinurl%3Aview+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky\"><span class=\"kw3\">View<\/span><\/a>.<span class=\"me1\">OnClickListener<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span>\n      @Override\n      <span class=\"kw1\">public<\/span> <span class=\"kw4\">void<\/span> onClick<span class=\"br0\">&#40;<\/span><a href=\"http:\/\/www.google.com\/search?hl=en&amp;q=allinurl%3Aview+java.sun.com&amp;btnI=I%27m%20Feeling%20Lucky\"><span class=\"kw3\">View<\/span><\/a> v<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span>\n        TextView number <span class=\"sy0\">=<\/span> numberText<span class=\"sy0\">;<\/span>\n        CharSequence text <span class=\"sy0\">=<\/span> number.<span class=\"me1\">getText<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span>\n        <span class=\"co1\">\/\/ This is annoyingly ugly, but is from the Android source<\/span>\n        <span class=\"kw1\">if<\/span> <span class=\"br0\">&#40;<\/span><span class=\"sy0\">!<\/span><span class=\"br0\">&#40;<\/span>text <span class=\"kw1\">instanceof<\/span> Editable<span class=\"br0\">&#41;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span>\n          number.<span class=\"me1\">setText<\/span><span class=\"br0\">&#40;<\/span>text, BufferType.<span class=\"me1\">EDITABLE<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span>\n        <span class=\"br0\">&#125;<\/span>\n        Editable editable <span class=\"sy0\">=<\/span> <span class=\"br0\">&#40;<\/span>Editable<span class=\"br0\">&#41;<\/span>number.<span class=\"me1\">getText<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span>\n        <span class=\"co1\">\/\/ Now that we have the editable, edit it.<\/span>\n        <span class=\"co1\">\/\/ This line is not from the Android source.<\/span>\n        <span class=\"kw1\">if<\/span> <span class=\"br0\">&#40;<\/span>text.<span class=\"me1\">length<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"sy0\">&gt;<\/span> 0<span class=\"br0\">&#41;<\/span> <span class=\"br0\">&#123;<\/span>\n          editable.<span class=\"me1\">delete<\/span><span class=\"br0\">&#40;<\/span>text.<span class=\"me1\">length<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"sy0\">-<\/span> 1, text.<span class=\"me1\">length<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span>\n        <span class=\"br0\">&#125;<\/span>\n      <span class=\"br0\">&#125;<\/span>\n    <span class=\"br0\">&#125;<\/span><span class=\"br0\">&#41;<\/span><span class=\"sy0\">;<\/span><\/pre><\/div><\/div>\n<p>That&#8217;s it! \u00a0 I tried another solution, having each soft-key fire a key event in the field.\u00a0  It worked, but since the field is automatically formatted, it didn&#8217;t feel right.\u00a0  Also, Android doesn&#8217;t let the application set the position of the cursor (caret) in the text view, so if the user re-opened the number input dialog to edit the number, the cursor appeared at the left side of the field, which meant that the user had to re-position the cursor before pressing any keys, which was a pain.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recently, I created a screen with soft keys that could be used for number input.\u00a0 Scroll down for some source. Everything was working perfectly.\u00a0 Each number key called field.append(&#8220;1&#8221;), passing in the correct number.\u00a0 Then, a text watcher would be notified of the change and reformat the field.\u00a0 The problem arose when I wrote the [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[57],"tags":[3,4,7],"class_list":["post-15","post","type-post","status-publish","format-standard","hentry","category-android","tag-android","tag-java","tag-redpoint"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1ql2n-f","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/15","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=15"}],"version-history":[{"count":16,"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/15\/revisions"}],"predecessor-version":[{"id":26,"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/15\/revisions\/26"}],"wp:attachment":[{"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=15"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=15"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/connorgarvey.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=15"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}