2010-07-15 6 views
13

두 개의 탭이있는 EditText와 TabHost가있는 레이아웃이 있습니다. Android 1.6.EditText와 TabHost는 서로를 좋아하지 않습니다.

다음과 같은 경우 하드웨어 키보드를 사용합니다. 재현 단계 :

  1. 활동이 표시되면 EditText가 포커스를 얻습니다.

  2. 아무 키나 누르면 EditText가 포커스를 잃고 첫 번째 탭에서 키를 얻습니다.

  3. 나는 EditText를 다시 클릭하고 타이핑을 시작합니다.

  4. 숫자 버튼을 누르지 않으면 작동합니다. 첫 번째 탭이 다시 초점을 얻습니다.

  5. 트랙볼이있는 EditText로 다시 스크롤합니다. 이제 아무 것도 입력 할 수 있습니다.

단계 2,3에서 초점이 맞춰진 EditText에서 트랙볼을 왼쪽/오른쪽으로 사용하면 EditText가 포커스를 잃게됩니다.

이것은 매우 이상합니다. 이걸 어떻게 처리할까요?

main.xml에 레이아웃 :

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" android:layout_height="fill_parent" 
    android:orientation="vertical"> 

    <EditText android:id="@+id/textfield" android:layout_width="fill_parent" 
     android:layout_height="wrap_content" android:maxLines="1" 
     android:lines="1" android:hint="Search" /> 

    <TabHost android:id="@android:id/tabhost" android:layout_width="fill_parent" 
     android:layout_height="wrap_content" android:layout_weight="1"> 

     <LinearLayout android:orientation="vertical" 
      android:layout_width="fill_parent" android:layout_height="fill_parent"> 


      <FrameLayout android:id="@android:id/tabcontent" 
       android:layout_width="fill_parent" android:layout_height="wrap_content" 
       android:layout_weight="1"> 

       <LinearLayout android:id="@+id/tab1" 
        android:layout_width="fill_parent" android:layout_height="wrap_content" /> 

       <LinearLayout android:id="@+id/tab2" 
        android:layout_width="fill_parent" android:layout_height="wrap_content" /> 

      </FrameLayout> 

      <TabWidget android:id="@android:id/tabs" 
       android:orientation="vertical" android:layout_width="fill_parent" 
       android:layout_height="wrap_content" android:gravity="bottom" /> 

     </LinearLayout> 

    </TabHost> 

</LinearLayout> 

활동 클래스 :

@Override 
    public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 
    TabHost tabs = (TabHost) this.findViewById(android.R.id.tabhost); 
    tabs.setup(); 

    Button vBtn; 

    TabSpec tspec1 = tabs.newTabSpec("label one"); 
    vBtn = new Button(this); 
    vBtn.setText("1"); 
    tspec1.setIndicator(vBtn); 
    tspec1.setContent(R.id.tab1); 

    TabSpec tspec2 = tabs.newTabSpec("label two"); 
    vBtn = new Button(this); 
    vBtn.setText("2"); 
    tspec2.setIndicator(vBtn); 
    tspec2.setContent(R.id.tab1); 

    tabs.addTab(tspec1); 
    tabs.addTab(tspec2); 
    } 

답변

1

당신은 정말 설정에 XML 레이아웃 파일에 탭을 TabHost 및 Tabwidget를 사용하지 않습니다.

나는 TabActivity를 확장하는 클래스에서 코드를 통해 모든 작업을 수행했으며 각 탭에 대해 Activity를 해당 탭의 UI 레이아웃을 처리 한 XML 레이아웃 파일과 연관 시켰습니다.

다음은 TabActivity 클래스이며 Tab 레이블의 모양을 처리하는 background_selectors XML의 예입니다. 여기

package com.android.myapp; 

    import android.app.TabActivity; 
    import android.content.Intent; 
    import android.os.Bundle; 
    import android.view.KeyEvent; 
    import android.view.View; 
    import android.widget.TabHost; 
    import android.widget.TabHost.TabSpec; 

    public class myMainActivity extends TabActivity { 
     protected static TabActivity ty; 
     private static TabSpec tsFirst = null; 
     private static TabSpec tsSecond = null; 
     private static TabSpec tsThird = null; 
     private static TabSpec tsFourth = null; 
     private static TabHost mytabHost = null; 
     private static final String FIRST_TAB_NAME = "Tab1"; 
     private static final String SECOND_TAB_NAME = "Tab2"; 
     private static final String THIRD_TAB_NAME = "Tab3"; 
     private static final String FOURTH_TAB_NAME = "Tab4"; 

     @Override 
     protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     try {  
      Bundle extras = getIntent().getExtras(); 
      if (extras != null) { 
      String strQuitStatus = extras.getString("QUITTING"); 
      if (strQuitStatus.equals("TRUE")) { 

       // exit application 
       System.gc(); 
       myMainActivity.this.finish(); 
       return; 
      }// end if (strQuitStatus.equals("TRUE")) 
      }// end if (extras != null) 

      this.myTabScreenSetup(); 
     }// end try statements 
     catch (Exception error) { 
      MyErrorLog<Exception> errExcpError = new MyErrorLog<Exception>(
       myMainActivity.this); 
      errExcpError.addToLogFile(error, "myMainActivity.onCreate", ""); 
      errExcpError = null; 
     }// end try/catch (Exception error) 
     }// end onCreate 


     /** 
     * myTabScreenSetup method 
     * 
     * Sets up the main tab screen 
     * 
     * @return void 
     * 
     */ 
     protected void myTabScreenSetup() { 
     try { 
      mytabHost = getTabHost(); 
      mytabHost.setVisibility(View.GONE); 
      mytabHost.setCurrentTab(0); 
      mytabHost.clearAllTabs(); 

      // create TabSpec instances 
      tsFirst = mytabHost.newTabSpec(FIRST_TAB_NAME); 
      tsSecond = mytabHost.newTabSpec(SECOND_TAB_NAME); 
      tsThird = mytabHost.newTabSpec(THIRD_TAB_NAME); 
      tsFourth = mytabHost.newTabSpec(FOURTH_TAB_NAME); 


      // create page tabs 
      // First list tab 
      Intent intentFirstList = new Intent(myMainActivity.this, 
       FirstListView.class); 
      intentFirstList.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
      mytabHost.addTab(tsFirst.setIndicator(
       FIRST_TAB_NAME, 
       myMainActivity.this.getResources().getDrawable(
        R.drawable.first_tab_background_selectors)).setContent(
       intentFirstList)); 

      // Second list tab 
      Intent intentSecondList = new Intent(myMainActivity.this, 
       SecondListView.class); 
      intentSecondList.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
      mytabHost.addTab(tsSecond.setIndicator(
       SECOND_TAB_NAME, 
       myMainActivity.this.getResources().getDrawable(
        R.drawable.second_tab_background_selectors)).setContent(
       intentSecondList)); 

      // Third list tab 
      Intent intentThirdList = new Intent(myMainActivity.this, 
       ThirdListView.class); 
      intentThirdList.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
      mytabHost.addTab(tsThird.setIndicator(
       THIRD_TAB_NAME, 
       myMainActivity.this.getResources().getDrawable(
        R.drawable.third_tab_background_selectors)).setContent(
       intentThirdList)); 

      // Fourth tab 
      Intent intentFourthTab = new Intent(myMainActivity.this, 
       FourthListView.class); 
      intentFourthTab.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 
      mytabHost.addTab(tsFourth.setIndicator(
       FOURTH_TAB_NAME, 
       myMainActivity.this.getResources().getDrawable(
        R.drawable.fourth_tab_background_selectors)).setContent(
       intentFourthTab)); 

      // set active tab to the first tab 
      mytabHost.setCurrentTab(0); 
      mytabHost.setVisibility(View.VISIBLE); 
     } catch (Exception error) { 
      MyErrorLog<Exception> errExcpError = new MyErrorLog<Exception>(
       myMainActivity.this); 
      errExcpError.addToLogFile(error, "myMainActivity.myTabScreenSetup", ""); 
      errExcpError = null; 
     }// end try/catch (Exception error) 
     }// end myTabScreenSetup 

     /** 
     * onResume 
     * 
     * Called after onRestoreInstanceState(Bundle), onRestart(), or onPause(), for 
     * your activity to start interacting with the user. 
     * 
     * This is a good place to begin animations, open exclusive-access devices 
     * (such as the camera), etc. 
     * 
     * Per the conditional check results, cleanup is performed and then the 
     * program returns focus to the calling code. 
     * 
     */ 
     @Override 
     protected void onResume() { 
     super.onResume(); 
     try { 
      Bundle extras = getIntent().getExtras(); 
      if (extras != null) { 
      String strQuitStatus = extras.getString("QUITTING"); 

      if (strQuitStatus.equals("TRUE")) { 
       // exit application 
       System.gc(); 
       myMainActivity.this.finish(); 
       return; 
      }// end if (strQuitStatus.equals("TRUE")) 
      }// end if (extras != null)  
     }// end try 
     catch (Exception error) { 
      MyErrorLog<Exception> errExcpError = new MyErrorLog<Exception>(
       myMainActivity.this); 
      errExcpError.addToLogFile(error, "myMainActivity.onResume", ""); 
      errExcpError = null; 
     }// end try/catch (Exception error) 
     }// end onResume 

     /** 
     * onPause method 
     * 
     * Called as part of the activity lifecycle when an activity is going into the 
     * background, but has not (yet) been killed. The counterpart to onResume(). 
     * 
     * This callback is mostly used for saving any persistent state the activity 
     * is editing, to present a "edit in place" model to the user and making sure 
     * nothing is lost if there are not enough resources to start the new activity 
     * without first killing this one. 
     * 
     * This is also a good place to do things like stop animations and other 
     * things that consume a noticeable mount of CPU in order to make the switch 
     * to the next activity as fast as possible, or to close resources that are 
     * exclusive access such as the camera. 
     * 
     * Checks the current state. If in "QUIT" state, then the cleanup and finish 
     * code is executed. 
     * 
     * "SAVEINSTANCE" is used for when the screen orientation is changed. 
     * 
     * @return void 
     * 
     */ 
     @Override 
     protected void onPause() { 
     super.onPause(); 
     // The user is going somewhere else, so make sure their current 
     // changes are safely saved away in the provider. We don't need 
     // to do this if only editing. 
     try { 
      //onPause code if needed 
     }// end try 
     catch (Exception error) { 
      MyErrorLog<Exception> errExcpError = new MyErrorLog<Exception>(
       myMainActivity.this); 
      errExcpError.addToLogFile(error, "myMainActivity.onPause", ""); 
      errExcpError = null; 
     }// end try/catch (Exception error) 
     }// end onPause 

     /** 
     * myAppCleanup method 
     * 
     * Sets object variables to null 
     * 
     * @return void 
     * 
     */ 
     protected void myAppCleanup() { 
     /* Set object variables to null */ 
     tsFirst = null; 
     tsSecond = null; 
     tsThird = null; 
     tsFourth = null; 
     mytabHost = null; 

     System.gc(); 
     }// end myAppCleanup 

     /** 
     * onDestroy method 
     * 
     * Perform any final cleanup before an activity is destroyed. 
     * 
     * This can happen either because the activity is finishing (someone called 
     * finish() on it, or because the system is temporarily destroying this 
     * instance of the activity to save space. 
     * 
     * You can distinguish between these two scenarios with the isFinishing() 
     * method. 
     * 
     * Note: do not count on this method being called as a place for saving data! 
     * For example, if an activity is editing data in a content provider, those 
     * edits should be committed in either onPause() or 
     * onSaveInstanceState(Bundle), not here. 
     * 
     * This method is usually implemented to free resources like threads that are 
     * associated with an activity, so that a destroyed activity does not leave 
     * such things around while the rest of its application is still running. 
     * 
     * There are situations where the system will simply kill the activity's 
     * hosting process without calling this method (or any others) in it, so it 
     * should not be used to do things that are intended to remain around after 
     * the process goes away. 
     * 
     * Derived classes must call through to the super class's implementation of 
     * this method. If they do not, an exception will be thrown. 
     * 
     * @return void 
     * 
     */ 
     @Override 
     protected void onDestroy() { 
     // The user is going somewhere else, so make sure their current 
     // changes are safely saved away in the provider. We don't need 
     // to do this if only editing. 
     myAppCleanup(); 
     super.onDestroy(); 
     }// end onDestroy 

     /** 

    * onSaveInstanceState method 
    * 
    * This method is called before an activity may be killed so that when it 
    * comes back some time in the future it can restore its state. 
    * 
    * If called, this method will occur before onStop(). There are no guarantees 
    * about whether it will occur before or after onPause(). 
    * 
    * @param Bundle 
    *   savedInstanceState: Bundle in which to place your saved state. 
    * 
    * @return void 
    * 
    */ 
    @Override 
    public void onSaveInstanceState(Bundle savedInstanceState) { 
    // Save UI state changes to the savedInstanceState. 
    // This bundle will be passed to onCreate if the process is 
    // killed and restarted. 
    super.onSaveInstanceState(savedInstanceState); 
    try { 
     //onSaveInstanceState code as needed 
    }// end try 
    catch (Exception error) { 
     MyErrorLog<Exception> errExcpError = new MyErrorLog<Exception>(
      myMainActivity.this); 
     errExcpError.addToLogFile(error, 
      "myMainActivityw.onSaveInstanceState", "no prompt"); 
     errExcpError = null; 
    }// end try/catch (Exception error) 
    }// end onSaveInstanceState 


     /** 
     * onRestoreInstanceState method 
     * 
     * This method is called after onStart() when the activity is being 
     * re-initialized from a previously saved state, given here in state. 
     * 
     * Most implementations will simply use onCreate(Bundle) to restore their 
     * state, but it is sometimes convenient to do it here after all of the 
     * initialization has been done or to allow subclasses to decide whether to 
     * use your default implementation. 
     * 
     * The default implementation of this method performs a restore of any view 
     * state that had previously been frozen by onSaveInstanceState(Bundle). 
     * 
     * @param Bundle 
     *   savedInstanceState: the data most recently supplied in 
     *   onSaveInstanceState(Bundle). 
     * 
     * @return void 
     * 
     */ 
     @Override 
     protected void onRestoreInstanceState(Bundle savedInstanceState) { 
     super.onRestoreInstanceState(savedInstanceState); 
     // Restore UI state from the savedInstanceState. 
     // This bundle has also been passed to onCreate. 
     try { 
      //onRestoreInstanceState code as needed 
     }// end try 
     catch (Exception error) { 
      MyErrorLog<Exception> errExcpError = new MyErrorLog<Exception>(
       myMainActivity.this); 
      errExcpError.addToLogFile(error, 
       "myMainActivity.onRestoreInstanceState", ""); 
      errExcpError = null; 
     }// end try/catch (Exception error) 
     }// end onRestoreInstanceState 

     /** 
     * onStart method 
     * 
     * Perform actions before the Activity is shown 
     * 
     * @return void 
     * 
     */ 
     @Override 
     protected void onStart() { 
     super.onStart(); 
     /* Set object variables to null */ 
     try { 
      Bundle extras = getIntent().getExtras(); 
      if (extras != null) { 
      String strQuitStatus = extras.getString("QUITTING"); 
      if (strQuitStatus.equals("TRUE")) { 
       // exit application 
       System.gc(); 
       myMainActivity.this.finish(); 
       return; 
      }// end if (strQuitStatus.equals("TRUE")) 
      }// end if (extras != null) 
     } catch (Exception error) { 
      MyErrorLog<Exception> errExcpError = new MyErrorLog<Exception>(
       myMainActivity.this); 
      errExcpError.addToLogFile(error, "myMainActivity.onStart", "no alert"); 
      errExcpError = null; 
     }// end try/catch (Exception error) 
     }// end onStart 

     /** 
     * onStop method 
     * 
     * Perform actions when the Activity is hidden from view 
     * 
     * @return void 
     * 
     */ 
     @Override 
     protected void onStop() { 
     super.onStop(); 
     /* Set object variables to null */ 
     try {  
      if (mytabHost != null){ 
      mytabHost = null; 
      } 

      this.myAppCleanup(); 
     } catch (Exception error) { 
      MyErrorLog<Exception> errExcpError = new MyErrorLog<Exception>(
       myMainActivity.this); 
      errExcpError.addToLogFile(error, "myMainActivity.onStop", "no alert"); 
      errExcpError = null; 
     }// end try/catch (Exception error) 
     }// end onStop 

     /** 
     * onKeyDown method 
     * 
     * Executes code depending on what keyCode is pressed. 
     * 
     * @param int keyCode 
     * @param KeyEvent 
     *   event KeyEvent object 
     * 
     * @return true if the code completes execution, false otherwise 
     * 
     */ 
     public boolean onKeyDown(int keyCode, KeyEvent event) { 
     switch (keyCode) { 
     case KeyEvent.KEYCODE_BACK: 
      /* perform cleanup */ 
      myMainActivity.mytabHost = getTabHost(); 
      myMainActivity.mytabHost.clearAllTabs(); 

      /* call System.gc */ 
      System.gc(); 
      myMainActivity.this.finish(); 
      return true; 
     default: 
      return false; 
     } 
     }// end onKeyDown 
    }// end myMainActivity 

는 first_tab_background_selectors의 XML 파일입니다

?xml version="1.0" encoding="utf-8"?> 
<!-- Copyright (C) 2008 The Android Open Source Project 

    Licensed under the Apache License, Version 2.0 (the "License"); 
    you may not use this file except in compliance with the License. 
    You may obtain a copy of the License at 

      http://www.apache.org/licenses/LICENSE-2.0 

    Unless required by applicable law or agreed to in writing, software 
    distributed under the License is distributed on an "AS IS" BASIS, 
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    See the License for the specific language governing permissions and 
    limitations under the License. 
--> 

<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
    <!-- Non focused states --> 
    <item android:state_focused="false" 
      android:state_selected="false" 
      android:state_pressed="false" 
      android:drawable="@drawable/first_list_unselected_tab_icon" /> 

    <item android:state_focused="false" 
      android:state_selected="true" 
      android:state_pressed="false" 
      android:drawable="@drawable/first_list_selected_tab_icon" /> 

    <!-- Focused states --> 
    <item android:state_focused="true" 
      android:state_selected="true" 
      android:state_pressed="false" 
      android:drawable="@drawable/first_list_selected_tab_icon" /> 

    <item android:state_focused="true" 
      android:state_selected="false" 
      android:state_pressed="false" 
      android:drawable="@drawable/first_list_unselected_tab_icon" /> 

    <!-- Pressed --> 
    <item android:state_focused="true" 
      android:state_selected="true" 
      android:state_pressed="true" 
      android:drawable="@drawable/first_list_selected_tab_icon" /> 

    <item android:state_focused="false" 
      android:state_selected="true" 
      android:state_pressed="true" 
      android:drawable="@drawable/first_list_selected_tab_icon" /> 


    <item android:state_pressed="true" 
     android:drawable="@drawable/first_list_selected_tab_icon" /> 

    <item android:state_enabled="true" 
     android:drawable="@drawable/first_list_selected_tab_icon" /> 

    <item android:state_focused="true" 
     android:drawable="@drawable/first_list_selected_tab_icon" /> 

    <item android:drawable="@drawable/first_list_unselected_tab_icon" /> 
</selector> 

selected_tab_icon과 unselected_tab_icons은 안드로이드 개발자 가이드의 설명에 따라 크기, 해상도 및 기타 그래픽 지침에 따라, 이미지 파일을 .PNG 있습니다.

+3

실제 답변이 아닙니다. 그냥 다른 방법을 제안하면 작동하지 않습니다. 그리고 그 일은 : 당신이 TabActivity를 사용한다면 당신은 그 활동 바깥에 아무 것도 보여줄 수 없다는 것입니다. 예 : 질문에서 그는 어떤 탭이 선택 되더라도 EditText를 볼 수 있기를 원합니다. 그것은 TabActivity에서는 불가능합니다. –

8

안드로이드 소스를 통해 광범위하게 파헤친 버그를 찾았습니다. 은 터치 모드를 떠날 때 (다른 사람이 키를 누를 때) 초점을 훔치는 onAttachedToWindow()에 OnTouchModeChangeListener를 등록합니다.

해결 방법을 만들었습니다 : 이것을 src/info/staticfree/workarounds/TabPatchView.java에 넣고 탭을 사용하는 레이아웃에이보기를 추가하십시오. 자세한 내용은 아래 Javadoc을 참조하십시오.

package info.staticfree.workarounds; 

import android.content.Context; 
import android.util.AttributeSet; 
import android.view.View; 
import android.widget.TabHost; 

/** 
* <p> 
* This is a workaround for a bug in Android in which using tabs such that all the content of the 
* layout isn't in the tabs causes the TabHost to steal focus from any views outside the tab 
* content. This is most commonly found with EditTexts. 
* </p> 
* <p> 
* To use, simply place this view in your @android:id/tablayout frameview: 
* </p> 
* 
* <pre> 
* <code> 
*    &lt;FrameLayout 
*     android:id="@android:id/tabcontent" 
*     android:layout_width="match_parent" 
*     android:layout_height="match_parent" > 
* 
*     &lt;info.staticfree.workarounds.TabPatchView 
*      android:layout_width="0dip" 
*      android:layout_height="0dip" /> 
*      
*      [your actual content goes here] 
*      &lt;/FrameLayout> 
* 
* </pre> 
* 
* </code> 
* 
* @author <a href="mailto:[email protected]">Steve Pomeroy</a> 
* @see <a href="http://code.google.com/p/android/issues/detail?id=2516">issue 2516</a> 
*/ 
public class TabPatchView extends View { 

    public TabPatchView(Context context) { 
     super(context); 
    } 

    public TabPatchView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public TabPatchView(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs, defStyle); 
    } 

    @Override 
    protected void onAttachedToWindow() { 
     super.onAttachedToWindow(); 
     final TabHost tabhost = (TabHost) getRootView().findViewById(android.R.id.tabhost); 
     tabhost.getViewTreeObserver().removeOnTouchModeChangeListener(tabhost); 
    } 
} 

SDK 12 이상을 타겟팅하는 경우 OnAttachStateChangeListener을 대신 사용할 수 있습니다.onCreate()에 다음을 추가하십시오.

TabHost mTabHost = (TabHost) findViewById(android.R.id.tabhost); 
mTabHost.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { 

    @Override 
    public void onViewDetachedFromWindow(View v) {} 

    @Override 
    public void onViewAttachedToWindow(View v) { 
     mTabHost.getViewTreeObserver().removeOnTouchModeChangeListener(mTabHost); 
    } 
}); 

그리고 그게 전부입니다!

+0

신의 축복이 내 친구 야! – Ivan

관련 문제