2012-02-21 27 views
2

我正在自定義一個Android control。這種控制使用包含一個編輯字段合併佈局(絨毛離開了):Android:嵌入式控件在乘法包含的複合控件中恢復到相同狀態

<merge xmlns:android="http://schemas.android.com/apk/res/android"> 
    ... 
    <EditText android:id="@+id/numberpicker_input"> 
     ... 
    /> 
    ... 
    </merge> 

這對於一個單一的控制工作正常。但是,如果我將這個控件的多個實例放置在佈局中,則在將設備從縱向旋轉到橫向時會導致奇怪的行爲。所有控件都會得到相同的值並將回調附加到錯誤的控件實例上。

經過檢查,發現嵌入的編輯控件具有完全相同的id值。

相關的Java源看起來是這樣的:

public class NumberPicker extends LinearLayout implements OnClickListener, 
     OnFocusChangeListener, OnLongClickListener, OnEditorActionListener { 

    private final EditText mText; 
.... 
    public NumberPicker(Context context, AttributeSet attrs, int defStyle) { 
     super(context, attrs); 

     Log.d(TAG, "Numberpicker create, have id: " + getId()); 

     setOrientation(VERTICAL); 
     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     inflater.inflate(R.layout.number_picker, this, true); 
... 

     mText = (EditText) findViewById(R.id.numberpicker_input); 
...  
    } 
} 

現在,考慮R.id.numberpicker_input的價值,當你有一個佈局的NumberPicker三個獨立的實例。在每種情況下,edittext字段將具有相同的ID。

爲了使自定義控件正常工作,我需要嵌入編輯控件的ID是唯一的。我怎樣才能解決這個問題?

回答

3

這是另一個選項,onSave/RestoreInstanceState()。以通常的方式執行此操作不起作用,因爲框架將在調用onRestoreInstanceState()後立即恢復EditText值,覆蓋您已恢復的任何內容。

爲了避免這種情況,清除嵌入式控制的id在該化合物控制的構造,例如:

mText.setId(NO_ID); 

ID爲組佈局元件,同時保存/恢復被忽略。

現在,保存和恢復在該化合物控制狀態中的嵌入式控制值:

protected Parcelable onSaveInstanceState() { 
     Parcelable p = super.onSaveInstanceState(); 
     Bundle bundle = new Bundle(); 

     bundle.putString("EDIT_TEXT", mText.getText().toString()); 
     bundle.putParcelable("SUPER", p); 

     return bundle; 
} 


protected void onRestoreInstanceState(Parcelable parcel) { 
    Bundle bundle = (Bundle) parcel; 

    mText.setText(bundle.getString("EDIT_TEXT")); 

    super.onRestoreInstanceState(bundle.getParcelable("SUPER")); 
} 

所恢復的狀態是特定的複合對象的給定實例,因此會有在多個實例之間沒有干擾相同的看法。

這個wrt的優點。以前的回答是,不需要擺弄id's。

+0

另一種禁用更明確的自動狀態保存的方法是在佈局[或調用View.setSaveEnabled()]中設置android:saveEnabled =「false」。這也允許View ID被有效地設置和使用。 – 2013-11-20 03:17:56

1

您可以創建XML資源唯一的ID如下:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 
    <item type="id" name="firstButton"/> 
    <item type="id" name="secondButton"/> 
    <item type="id" name="thirdButton"/> 
</resources> 

和代碼,編程設計你EditTexts並設置其ID,如:

txt1.setId(R.id.firstButton); 
txt2.setId(R.id.secondButton); 
txt3.setId(R.id.thirdButton); 

更多的信息,請閱讀this


更新.....

另一種辦法是通過各方面的意見進行迭代,並自行設置唯一的ID,並保持這些分配的ID的軌跡:

首先,設置一個ID在XML文件中的父佈局。它將幫助我們檢索整個佈局,如ViewGroup。在這個例子中,我把我的LinearLayout的ID parentLayout

@Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.test_activity); 

     ViewGroup parent = (ViewGroup)findViewById(R.id.parentLayout); 

     for(int i=0; i<parent.getChildCount(); i++){ 
      //at this point i dont know the Id of edittext yet 
      View v = parent.getChildAt(i); 

      //if the view is an instance of EditText then lets set to it a new Id 
      if(v instanceof EditText){ 
       EditText et = (EditText) v; 

       et.setId(123456); 

       Toast.makeText(this,"EditText's id: " + et.getId(), 
         Toast.LENGTH_SHORT).show(); 
      } 
     } 
    } 

我猜使用這種技術,你甚至可以指定一些隨機ID來所有的意見,以避免任何標識衝突。

+0

謝謝你教我新的東西;但它不能解決問題。這僅僅意味着我爲每個嵌入式控件重新分配了相同的唯一ID。有沒有一種方法來分配身份證,並記住它們的價值超過銷燬/創建事件? – 2012-02-21 15:15:07

+0

您是否爲您的控件創建了單獨的ID?如果它沒有幫助,然後粘貼你的代碼,所以我可以有一個更好的主意,你的確切問題 – waqaslam 2012-02-22 09:42:11

+0

增加了一些問題的來源,希望這可以幫助。 – 2012-02-22 12:49:03

0

我一直在尋找在Android庫中的code for saving instance state。我注意到,控制/佈局ID被用作保存狀態下的鍵。我認爲這裏存在這個問題:複合對象的多個實例中嵌入元素的id是相同的。所以在保存時,只保存一個嵌入元素的狀態,其餘的都被丟棄。

我認爲解決方案在於使嵌入式元素的ID唯一。我的解決辦法是這樣的:

定義在通過用於嵌入式控制的ID的屬性:

<resources> 
    <declare-styleable name="compoundcontrol"> 
     <attr name="idEmbedded" format="reference" /> 
    </declare-styleable> 
</resources> 

通行證ID作爲參數來在佈局中化合物控制:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:np="http://schemas.android.com/apk/res/org.example.compoundcontrol" 
... 
/> 
... 
    <org.example.compoundcontrol.CompoundControl 
     android:id = "@+id/compound_id" 
     np:idEmbedded="@+id/embedded_id" 
    /> 
... 

在複合對象的構造函數,讀入此屬性值並使用它來設置嵌入式控件的ID。

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

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.compoundcontrol); 
    int new_id = a.getResourceId(R.styleable.compoundcontrol_idEmbedded, DEFAULT_VALUE);   
    a.recycle(); 

    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    inflater.inflate(R.layout.compoundcontrol, this, true); 

    View mEmbedded = findViewById(R.id.embedded_control); 
    mEmbedded.setId(new_id); 
} 

不幸的是,嵌入式控件的ID必須明確設置,但它的確有用。

0

獲得新資源ID的另一種可能性是這個小小的破解。

public class MyBaseDialogFragment extends DialogFragment implements 
    ResourceIdGetter { 
Random mRandom; 
@Override 
public int getNewResourceId() { 
    if(mRandom == null) 
     mRandom = new Random(123456789); // make them predictable 
    int i; 
    while (true) { 
     i = mRandom.nextInt(); 
     if(i <= 0) 
      continue; 
     try { 
      getResources().getResourceName(i); 
     } catch (NotFoundException e) { 
      break; 
     } 
    } 
    return i; 
} 
} 

之後,您可以重新分配新的唯一資源ID的對照與IDS:

protected mNewResorceId; 
public void addEditViewToLinearLayout(LinearLayout pContainer, 
      LayoutInflater pInflater, ResourceIdGetter pRes) { 

    View a_multiple_inflated_layout = pInflater.inflate(
     R.layout.a_multiple_inflated_layout, pContainer, false); 
    mNewResorceId = pRes.getNewResourceId() 
    a_multiple_inflated_layout.findViewById(R.id.child_view_with_id).setId(
       mNewResorceId); 

    pContainer.addView(a_multiple_inflated_layout, new LayoutParams(
    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 
} 

之後,你必須使用findViewById(mNewResorceId)來獲得視圖。 由於Random的給定種子,分配的數字總是相同的。這意味着,即使活動被破壞並重新創建,光標位置等內容也會被恢復。