UserDictionaryToolsList.java revision 77ffa9b0b986a2d70143f63cdaa8451bf1674f84
1/* 2 * Copyright (C) 2008,2009 OMRON SOFTWARE Co., Ltd. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package jp.co.omronsoft.openwnn; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.Dialog; 22import android.content.DialogInterface; 23import android.content.Intent; 24import android.graphics.Color; 25import android.os.Bundle; 26import android.os.Handler; 27import android.text.TextUtils; 28import android.util.Log; 29import android.view.Display; 30import android.view.Menu; 31import android.view.MenuItem; 32import android.view.MotionEvent; 33import android.view.KeyEvent; 34import android.view.View; 35import android.view.ViewGroup; 36import android.view.Window; 37import android.view.WindowManager; 38import android.view.View.OnFocusChangeListener; 39import android.view.View.OnTouchListener; 40import android.widget.TableLayout; 41import android.widget.TableRow; 42import android.widget.TextView; 43import android.widget.Toast; 44 45/** 46 * The abstract class for user dictionary tool. 47 * 48 * @author Copyright (C) 2009, OMRON SOFTWARE CO., LTD. All Rights Reserved. 49 */ 50public abstract class UserDictionaryToolsList extends Activity 51 implements View.OnClickListener, OnTouchListener, OnFocusChangeListener { 52 53 /** The class name of the user dictionary tool */ 54 protected String mListViewName; 55 /** The class name of the user dictionary editor */ 56 protected String mEditViewName; 57 /** The package name of the user dictionary editor */ 58 protected String mPackageName; 59 60 /** ID of the menu item (add) */ 61 private final int MENU_ITEM_ADD = 0; 62 /** ID of the menu item (edit) */ 63 private final int MENU_ITEM_EDIT = 1; 64 /** ID of the menu item (delete) */ 65 private final int MENU_ITEM_DELETE = 2; 66 /** ID of the menu item (initialize) */ 67 private final int MENU_ITEM_INIT = 3; 68 69 /** ID of the dialog control (confirm deletion) */ 70 private final int DIALOG_CONTROL_DELETE_CONFIRM = 0; 71 /** ID of the dialog control (confirm initialize) */ 72 private final int DIALOG_CONTROL_INIT_CONFIRM = 1; 73 74 /** The size of font*/ 75 private final int WORD_TEXT_SIZE = 16; 76 77 /** The color of background */ 78 private final int UNFOCUS_BACKGROUND_COLOR = 0xFF242424; 79 private final int FOCUS_BACKGROUND_COLOR = 0xFFFF8500; 80 81 /** The minimum count of registered words */ 82 private final int MIN_WORD_COUNT = 0; 83 /** The maximum count of registered words */ 84 private final int MAX_WORD_COUNT = 100; 85 /** Maximum word count to display */ 86 private final int MAX_LIST_WORD_COUNT = 50; 87 /** Maximum word count to display (at the first step) */ 88 private final int MAX_LIST_WORD_DELAY_COUNT = 50; 89 90 /** The threshold time of the double tapping */ 91 private final int DOUBLE_TAP_TIME = 300; 92 93 /** Widgets which constitute this screen of activity */ 94 private Menu mMenu; 95 private TableLayout mTableLayout; 96 private static View sFocusingView = null; 97 private static View sFocusingPairView = null; 98 99 /** Objects which control state transitions */ 100 private Intent mIntent; 101 private OpenWnnEvent mEvent; 102 private Handler mDelayUpdateHandler; 103 104 /** The number of the registered words */ 105 private int mWordCount = -1; 106 107 /** The state of menu items */ 108 private boolean mAddMenuEnabled; 109 private boolean mEditMenuEnabled; 110 private boolean mDeleteMenuEnabled; 111 private boolean mInitMenuEnabled; 112 113 /** {@code true} if the menu option is initialized */ 114 private boolean mInitializedMenu = false; 115 /** {@code true} if one of word is selected */ 116 private boolean mSelectedWords; 117 /** The viewID which is selected */ 118 private int mSelectedViewID = -1; 119 /** The viewID which was selected previously */ 120 private static int sBeforeSelectedViewID = -1; 121 /** The time of previous action */ 122 private static long sJustBeforeActionTime = -1; 123 124 private boolean mHasCreatedList = false; 125 126 /** 127 * Send the specified event to IME 128 * 129 * @param ev The event object 130 * @return {@code true} if this event is processed 131 */ 132 protected abstract boolean sendEventToIME(OpenWnnEvent ev); 133 134 /** 135 * Create the header 136 */ 137 protected abstract void headerCreate(); 138 139 /** @see android.app.Activity#onCreate */ 140 @Override protected void onCreate(Bundle savedInstanceState) { 141 142 143 super.onCreate(savedInstanceState); 144 145 mDelayUpdateHandler = new Handler(); 146 /* create XML layout */ 147 requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); 148 setContentView(R.layout.user_dictionary_tools_list); 149 150 } 151 152 /** @see android.app.Activity#onStart */ 153 @Override protected void onStart() { 154 super.onStart(); 155 sBeforeSelectedViewID = -1; 156 sJustBeforeActionTime = -1; 157 updateWordList(); 158 } 159 160 /** @see android.app.Activity#onStop */ 161 @Override protected void onStop() { 162 super.onStop(); 163 mDelayUpdateHandler.removeCallbacks(updateWordListRunnable); 164 } 165 166 /** 167 * Set parameters of table 168 * 169 * @param w The width of the table 170 * @param h The height of the table 171 * @return The information of the layout 172 */ 173 private TableLayout.LayoutParams tableCreateParam(int w, int h) { 174 return new TableLayout.LayoutParams(w, h); 175 } 176 177 /** @see android.app.Activity#onCreateOptionsMenu */ 178 @Override public boolean onCreateOptionsMenu(Menu menu) { 179 180 181 /* initialize the menu */ 182 menu.clear(); 183 /* set the menu item enable/disable */ 184 setOptionsMenuEnabled(); 185 /* [menu] add a word */ 186 menu.add(0, MENU_ITEM_ADD, 0, R.string.user_dictionary_add) 187 .setIcon(android.R.drawable.ic_menu_add) 188 .setEnabled(mAddMenuEnabled); 189 /* [menu] edit a word */ 190 menu.add(0, MENU_ITEM_EDIT, 0, R.string.user_dictionary_edit) 191 .setIcon(android.R.drawable.ic_menu_edit) 192 .setEnabled(mEditMenuEnabled); 193 /* [menu] delete a word */ 194 menu.add(0, MENU_ITEM_DELETE, 0, R.string.user_dictionary_delete) 195 .setIcon(android.R.drawable.ic_menu_delete) 196 .setEnabled(mDeleteMenuEnabled); 197 /* [menu] clear the dictionary */ 198 menu.add(1, MENU_ITEM_INIT, 0, R.string.user_dictionary_init) 199 .setIcon(android.R.drawable.ic_menu_delete) 200 .setEnabled(mInitMenuEnabled); 201 202 mMenu = menu; 203 mInitializedMenu = true; 204 205 206 return super.onCreateOptionsMenu(menu); 207 } 208 209 /** 210 * Change state of the option menus according to a current state of the list widget 211 */ 212 private void setOptionsMenuEnabled() { 213 214 215 /* [menu] add a word */ 216 if ((mWordCount >= MAX_WORD_COUNT) || !mHasCreatedList) { 217 /* disable if the number of registered word exceeds MAX_WORD_COUNT */ 218 mAddMenuEnabled = false; 219 } else { 220 mAddMenuEnabled = true; 221 } 222 223 /* [menu] edit a word/delete a word */ 224 if (mWordCount <= MIN_WORD_COUNT) { 225 /* disable if no word is registered or no word is selected */ 226 mEditMenuEnabled = false; 227 mDeleteMenuEnabled = false; 228 } else { 229 mEditMenuEnabled = true; 230 mDeleteMenuEnabled = true; 231 } 232 233 /* [menu] clear the dictionary (always enabled) */ 234 mInitMenuEnabled = true; 235 236 } 237 238 /** @see android.app.Activity#onOptionsItemSelected */ 239 @Override public boolean onOptionsItemSelected(MenuItem item) { 240 241 boolean ret; 242 switch (item.getItemId()) { 243 case MENU_ITEM_ADD: 244 /* add a word */ 245 wordAdd(); 246 ret = true; 247 break; 248 249 case MENU_ITEM_EDIT: 250 /* edit the word (show dialog) */ 251 wordEdit(sFocusingView, sFocusingPairView); 252 ret = true; 253 break; 254 255 case MENU_ITEM_DELETE: 256 /* delete the word (show dialog) */ 257 showDialog(DIALOG_CONTROL_DELETE_CONFIRM); 258 ret = true; 259 break; 260 261 case MENU_ITEM_INIT: 262 /* clear the dictionary (show dialog) */ 263 showDialog(DIALOG_CONTROL_INIT_CONFIRM); 264 ret = true; 265 break; 266 267 default: 268 ret = false; 269 } 270 271 return ret; 272 } 273 274 /** @see android.app.Activity#onKeyUp */ 275 @Override public boolean onKeyUp(int keyCode, KeyEvent event) { 276 /* open the menu if KEYCODE_DPAD_CENTER is pressed */ 277 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { 278 openOptionsMenu(); 279 return true; 280 } 281 return false; 282 } 283 284 /** @see android.app.Activity#onCreateDialog */ 285 @Override protected Dialog onCreateDialog(int id) { 286 switch (id) { 287 case DIALOG_CONTROL_DELETE_CONFIRM: 288 return new AlertDialog.Builder(UserDictionaryToolsList.this) 289 .setMessage(R.string.user_dictionary_delete_confirm) 290 .setNegativeButton(android.R.string.cancel, null) 291 .setPositiveButton(android.R.string.ok, mDialogDeleteWords) 292 .setCancelable(true) 293 .create(); 294 295 case DIALOG_CONTROL_INIT_CONFIRM: 296 return new AlertDialog.Builder(UserDictionaryToolsList.this) 297 .setMessage(R.string.dialog_clear_user_dictionary_message) 298 .setNegativeButton(android.R.string.cancel, null) 299 .setPositiveButton(android.R.string.ok, mDialogInitWords) 300 .setCancelable(true) 301 .create(); 302 303 default: 304 Log.e("OpenWnn", "onCreateDialog : Invaled Get DialogID. ID=" + id); 305 break; 306 } 307 308 309 return super.onCreateDialog(id); 310 } 311 312 /** 313 * Process the event when the button on the "Delete word" dialog is pushed 314 * 315 * @param dialog The information of the dialog 316 * @param button The button that is pushed 317 */ 318 private DialogInterface.OnClickListener mDialogDeleteWords = 319 new DialogInterface.OnClickListener() { 320 public void onClick(DialogInterface dialog, int button) { 321 322 CharSequence focusString = ((TextView)sFocusingView).getText(); 323 CharSequence focusPairString = ((TextView)sFocusingPairView).getText(); 324 WnnWord wnnWordSearch = new WnnWord(); 325 326 if (mSelectedViewID > MAX_WORD_COUNT) { 327 wnnWordSearch.stroke = focusPairString.toString(); 328 wnnWordSearch.candidate = focusString.toString(); 329 } else { 330 wnnWordSearch.stroke = focusString.toString(); 331 wnnWordSearch.candidate = focusPairString.toString(); 332 } 333 boolean deleted = deleteWord(wnnWordSearch); 334 if (deleted) { 335 Toast.makeText(getApplicationContext(), 336 R.string.user_dictionary_delete_complete, 337 Toast.LENGTH_LONG).show(); 338 } else { 339 Toast.makeText(getApplicationContext(), 340 R.string.user_dictionary_delete_fail, 341 Toast.LENGTH_LONG).show(); 342 return; 343 } 344 345 int id = mSelectedViewID; 346 id = (MAX_WORD_COUNT < id) ? id - MAX_WORD_COUNT : id; 347 View v = null; 348 349 mTableLayout.removeView((View)sFocusingView.getParent()); 350 351 for (int i = id; i < MAX_WORD_COUNT; i++) { 352 v = mTableLayout.findViewById(i); 353 if (v != null) { 354 break; 355 } 356 } 357 358 if (v == null) { 359 for (int i = id; 0 <= i; i--) { 360 v = mTableLayout.findViewById(i); 361 if (v != null) { 362 break; 363 } 364 } 365 } 366 367 if (v != null) { 368 ((View)v.getParent()).requestFocus(); 369 } 370 mWordCount--; 371 372 TextView leftText = (TextView) findViewById(R.id.user_dictionary_tools_list_title_words_count); 373 leftText.setText(mWordCount + "/" + MAX_WORD_COUNT); 374 375 if (mInitializedMenu) { 376 onCreateOptionsMenu(mMenu); 377 } 378 } 379 }; 380 381 /** 382 * Process the event when the button on the "Initialize" dialog is pushed 383 * 384 * @param dialog The information of the dialog 385 * @param button The button that is pushed 386 */ 387 private DialogInterface.OnClickListener mDialogInitWords = 388 new DialogInterface.OnClickListener() { 389 public void onClick(DialogInterface dialog, int button) { 390 391 /* clear the user dictionary */ 392 OpenWnnEvent ev = new OpenWnnEvent(OpenWnnEvent.INITIALIZE_USER_DICTIONARY, new WnnWord()); 393 394 sendEventToIME(ev); 395 /* show the message */ 396 Toast.makeText(getApplicationContext(), R.string.dialog_clear_user_dictionary_done, 397 Toast.LENGTH_LONG).show(); 398 updateWordList(); 399 if (mInitializedMenu) { 400 onCreateOptionsMenu(mMenu); 401 } 402 } 403 }; 404 405 406 /** @see android.view.View.OnClickListener#onClick */ 407 public void onClick(View arg0) { 408 } 409 410 /** @see android.view.View.OnTouchListener#onTouch */ 411 public boolean onTouch(View v, MotionEvent e) { 412 413 414 mSelectedViewID = ((TextView)v).getId(); 415 switch (e.getAction()) { 416 case MotionEvent.ACTION_DOWN: 417 /* double tap handling */ 418 if (sBeforeSelectedViewID != ((TextView)v).getId()) { 419 /* save the view id if the id is not same as previously selected one */ 420 sBeforeSelectedViewID = ((TextView)v).getId(); 421 } else { 422 if ((e.getDownTime() - sJustBeforeActionTime) < DOUBLE_TAP_TIME) { 423 /* edit the word if double tapped */ 424 sFocusingView = v; 425 sFocusingPairView = ((UserDictionaryToolsListFocus)v).getPairView(); 426 wordEdit(sFocusingView, sFocusingPairView); 427 } 428 } 429 /* save the action time */ 430 sJustBeforeActionTime = e.getDownTime(); 431 break; 432 } 433 434 return false; 435 } 436 437 /** @see android.view.View.OnFocusChangeListener#onFocusChange */ 438 public void onFocusChange(View v, boolean hasFocus) { 439 440 mSelectedViewID = ((TextView)v).getId(); 441 sFocusingView = v; 442 sFocusingPairView = ((UserDictionaryToolsListFocus)v).getPairView(); 443 if (hasFocus) { 444 ((TextView)v).setTextColor(Color.BLACK); 445 v.setBackgroundColor(FOCUS_BACKGROUND_COLOR); 446 ((TextView)sFocusingPairView).setTextColor(Color.BLACK); 447 sFocusingPairView.setBackgroundColor(FOCUS_BACKGROUND_COLOR); 448 mSelectedWords = true; 449 } else { 450 if (mSelectedViewID == 0) { 451 mSelectedWords = false; 452 } 453 ((TextView)v).setTextColor(Color.LTGRAY); 454 v.setBackgroundColor(UNFOCUS_BACKGROUND_COLOR); 455 ((TextView)sFocusingPairView).setTextColor(Color.LTGRAY); 456 sFocusingPairView.setBackgroundColor(UNFOCUS_BACKGROUND_COLOR); 457 } 458 if (mInitializedMenu) { 459 onCreateOptionsMenu(mMenu); 460 } 461 } 462 463 /** 464 * Add the word 465 */ 466 public void wordAdd() { 467 screenTransition(Intent.ACTION_INSERT, mEditViewName); 468 } 469 470 /** 471 * Edit the specified word 472 * 473 * @param focusView The information of view 474 * @param focusPairView The information of pair of view 475 */ 476 public void wordEdit(View focusView, View focusPairView) { 477 if (mSelectedViewID > MAX_WORD_COUNT) { 478 createUserDictionaryToolsEdit(focusPairView, focusView); 479 } else { 480 createUserDictionaryToolsEdit(focusView, focusPairView); 481 } 482 screenTransition(Intent.ACTION_EDIT, mEditViewName); 483 } 484 485 /** 486 * The internal process of editing the specified word 487 * 488 * @param focusView The information of view 489 * @param focusPairView The information of pair of view 490 */ 491 protected abstract UserDictionaryToolsEdit createUserDictionaryToolsEdit(View focusView, View focusPairView); 492 493 /** 494 * Delete the specified word 495 * 496 * @param searchword The information of searching 497 * @return {@code true} if success; {@code false} if fail. 498 */ 499 public boolean deleteWord(WnnWord searchword) { 500 OpenWnnEvent event = new OpenWnnEvent(OpenWnnEvent.LIST_WORDS_IN_USER_DICTIONARY, 501 WnnEngine.DICTIONARY_TYPE_USER, 502 searchword); 503 504 boolean deleted = false; 505 sendEventToIME(event); 506 for( int i=0; i < MAX_WORD_COUNT; i++) { 507 WnnWord getword = new WnnWord(); 508 event = new OpenWnnEvent(OpenWnnEvent.GET_WORD, 509 getword); 510 sendEventToIME(event); 511 getword = event.word; 512 int len = getword.candidate.length(); 513 if (len == 0) { 514 break; 515 } 516 if (searchword.candidate.equals(getword.candidate)) { 517 WnnWord delword = new WnnWord(); 518 delword.stroke = searchword.stroke; 519 delword.candidate = searchword.candidate; 520 delword.id = i; 521 event = new OpenWnnEvent(OpenWnnEvent.DELETE_WORD, 522 delword); 523 deleted = sendEventToIME(event); 524 break; 525 } 526 } 527 528 if (mInitializedMenu) { 529 onCreateOptionsMenu(mMenu); 530 } 531 532 return deleted; 533 } 534 535 536 /** 537 * Processing the transition of screen 538 * 539 * @param action The string of action 540 * @param classname The class name 541 */ 542 private void screenTransition(String action, String classname) { 543 544 if (action.equals("")) { 545 mIntent = new Intent(); 546 } else { 547 mIntent = new Intent(action); 548 } 549 mIntent.setClassName(mPackageName, classname); 550 startActivity(mIntent); 551 finish(); 552 } 553 554 555 /** 556 * Update the word list. 557 */ 558 private void updateWordList() { 559 mWordCount = 0; 560 WnnWord wnnWordSearch = new WnnWord(); 561 mEvent = new OpenWnnEvent(OpenWnnEvent.LIST_WORDS_IN_USER_DICTIONARY, 562 WnnEngine.DICTIONARY_TYPE_USER, 563 wnnWordSearch); 564 sendEventToIME(mEvent); 565 566 mTableLayout = (TableLayout)findViewById(R.id.user_dictionary_tools_table); 567 mTableLayout.removeAllViews(); 568 569 if (createWordList(mWordCount,MAX_LIST_WORD_COUNT, this)) { 570 headerCreate(); 571 final TextView leftText = (TextView) findViewById(R.id.user_dictionary_tools_list_title_words_count); 572 leftText.setText(R.string.user_dictionary_creating_wordlist); 573 mDelayUpdateHandler.removeCallbacks(updateWordListRunnable); 574 mDelayUpdateHandler.postDelayed(updateWordListRunnable, 0); 575 } else { 576 if (mSelectedViewID >= 0) { 577 View v; 578 v = mTableLayout.findViewById(mSelectedViewID); 579 if (v != null) { 580 ((View)v.getParent()).requestFocus(); 581 } else { 582 mSelectedViewID--; 583 if (mSelectedViewID < 0) { 584 mSelectedViewID = 0; 585 } 586 v = mTableLayout.findViewById(mSelectedViewID); 587 if (v != null) { 588 ((View)v.getParent()).requestFocus(); 589 } 590 } 591 } 592 headerCreate(); 593 final TextView leftText = (TextView) findViewById(R.id.user_dictionary_tools_list_title_words_count); 594 leftText.setText(mWordCount + "/" + MAX_WORD_COUNT); 595 mHasCreatedList = true; 596 } 597 } 598 599 /** 600 * Handler for updating the word list. 601 */ 602 private final Runnable updateWordListRunnable = new Runnable() { 603 public void run() { 604 UserDictionaryToolsList self = UserDictionaryToolsList.this; 605 if (createWordList(mWordCount,mWordCount+MAX_LIST_WORD_DELAY_COUNT, self)) { 606 mDelayUpdateHandler.removeCallbacks(updateWordListRunnable); 607 mDelayUpdateHandler.postDelayed(updateWordListRunnable, 0); 608 } else { 609 mDelayUpdateHandler.removeCallbacks(updateWordListRunnable); 610 final TextView leftText = (TextView) findViewById(R.id.user_dictionary_tools_list_title_words_count); 611 leftText.setText(mWordCount + "/" + MAX_WORD_COUNT); 612 mHasCreatedList = true; 613 if (mInitializedMenu) { 614 onCreateOptionsMenu(mMenu); 615 } 616 } 617 } 618 }; 619 620 /** 621 * Create the list of words. 622 * 623 * @param position Start position to create the list 624 * @param max Maximum number of words to display 625 * @param self UserDictionaryToolsList 626 * @return {@code true} if more words undisplayed; {@code false} if no more. 627 */ 628 private boolean createWordList(int position ,int max, UserDictionaryToolsList self) { 629 boolean ret = true; 630 631 if (position >= MAX_WORD_COUNT) { 632 return false; 633 } 634 Window window = getWindow(); 635 WindowManager windowManager = window.getWindowManager(); 636 Display display = windowManager.getDefaultDisplay(); 637 int system_width = display.getWidth(); 638 WnnWord wnnWordGet = new WnnWord(); 639 mEvent = new OpenWnnEvent(OpenWnnEvent.GET_WORD, wnnWordGet); 640 641 int i; 642 for (i = position; i < max; i++) { 643 if (!sendEventToIME(mEvent)) { 644 ret = false; 645 break; 646 } 647 wnnWordGet = mEvent.word; 648 int len_stroke = wnnWordGet.stroke.length(); 649 int len_candidate = wnnWordGet.candidate.length(); 650 if (len_stroke == 0 || len_candidate == 0) { 651 ret = false; 652 break; 653 } 654 655 mWordCount++; 656 TableRow row = new TableRow(self); 657 UserDictionaryToolsListFocus stroke = new UserDictionaryToolsListFocus(self); 658 stroke.setId(mWordCount); 659 stroke.setText(wnnWordGet.stroke); 660 stroke.setWidth(system_width/2); 661 stroke.setTextSize(WORD_TEXT_SIZE); 662 stroke.setTextColor(Color.LTGRAY); 663 stroke.setBackgroundColor(UNFOCUS_BACKGROUND_COLOR); 664 stroke.setSingleLine(); 665 stroke.setPadding(1,0,1,1); 666 stroke.setEllipsize(TextUtils.TruncateAt.END); 667 stroke.setClickable(true); 668 stroke.setFocusable(true); 669 stroke.setFocusableInTouchMode(true); 670 stroke.setOnTouchListener(self); 671 stroke.setOnFocusChangeListener(self); 672 673 UserDictionaryToolsListFocus candidate = new UserDictionaryToolsListFocus(self); 674 candidate.setId(mWordCount+MAX_WORD_COUNT); 675 candidate.setText(wnnWordGet.candidate); 676 candidate.setWidth(system_width/2); 677 candidate.setTextSize(WORD_TEXT_SIZE); 678 candidate.setTextColor(Color.LTGRAY); 679 candidate.setBackgroundColor(UNFOCUS_BACKGROUND_COLOR); 680 candidate.setSingleLine(); 681 candidate.setPadding(1,0,1,1); 682 candidate.setEllipsize(TextUtils.TruncateAt.END); 683 candidate.setClickable(true); 684 candidate.setFocusable(true); 685 candidate.setFocusableInTouchMode(true); 686 candidate.setOnTouchListener(self); 687 candidate.setOnFocusChangeListener(self); 688 689 stroke.setPairView(candidate); 690 candidate.setPairView(stroke); 691 692 row.addView(stroke); 693 row.addView(candidate); 694 mTableLayout.addView(row, tableCreateParam(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 695 } 696 mTableLayout.requestLayout(); 697 return ret; 698 } 699} 700