2014-08-30 72 views
0

我用下面的例子來自Android開發者網站:導致錯誤的Android SwipeRefreshLayout示例:「指定的孩子已經有父母。」

SwipeRefreshListFragment Official Example

我不斷收到運行應用程序時出現此錯誤:

java.lang.RuntimeException: Unable to start activity ComponentInfo{example.com.app/example.com.app.MainActivity}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. 
     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195) 
     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245) 
     at android.app.ActivityThread.access$800(ActivityThread.java:135) 
     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196) 
     at android.os.Handler.dispatchMessage(Handler.java:102) 
     at android.os.Looper.loop(Looper.java:136) 
     at android.app.ActivityThread.main(ActivityThread.java:5017) 
     at java.lang.reflect.Method.invokeNative(Native Method) 
     at java.lang.reflect.Method.invoke(Method.java:515) 
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779) 
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595) 
     at dalvik.system.NativeStart.main(Native Method) 
Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. 
     at android.view.ViewGroup.addViewInner(ViewGroup.java:3562) 
     at android.view.ViewGroup.addView(ViewGroup.java:3415) 
     at android.view.ViewGroup.addView(ViewGroup.java:3360) 
     at android.view.ViewGroup.addView(ViewGroup.java:3336) 
     at android.support.v4.app.NoSaveStateFrameLayout.wrap(NoSaveStateFrameLayout.java:40) 
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:942) 
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1115) 
     at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682) 
     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1478) 
     at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:570) 
     at example.com.taskdata.activities.ActivityBase.onStart(ActivityBase.java:23) 
     at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1171) 
     at android.app.Activity.performStart(Activity.java:5241) 
     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2168) 

的代碼是正確的,它與頁面上顯示的示例類似。我所有的課程都有適當的進口。我沒有在任何地方使用過兩次setContentView()。我需要在哪裏添加removeView()方法或者是否有更好的方法來完成此操作?

欣賞任何關於此的建議。我想知道爲什麼這個例子不起作用。

附件是我的代碼:

MainActivity

public class MainActivity extends ActivityBase { 

public static final String TAG = "MainActivity"; 

// Whether log fragment is shown 
private boolean mLogShown; 


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

    FragmentTransaction fragmentTransaction = getSupportFragmentManager() 
      .beginTransaction(); 
    TaskListFragment fragment = new TaskListFragment(); 
    fragmentTransaction.replace(R.id.container, fragment).commit(); 

    **MY GUESS IS THE PROBLEM IS HERE ^^^^** 
} 


@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.main, menu); 
    return true; 
} 

@Override 
public boolean onPrepareOptionsMenu(Menu menu) { 
    MenuItem logToggle = menu.findItem(R.id.action_settings); 
    logToggle.setVisible(findViewById(R.id.output) instanceof ViewAnimator); 
    logToggle.setTitle(mLogShown ? R.string.hide_log : R.string.show_log); 

    return super.onPrepareOptionsMenu(menu); 
} 

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    // Handle action bar item clicks here. The action bar will 
    // automatically handle clicks on the Home/Up button, so long 
    // as you specify a parent activity in AndroidManifest.xml. 
    switch (item.getItemId()) { 
     case R.id.action_settings: 
      mLogShown = !mLogShown; 
      ViewAnimator output = (ViewAnimator) findViewById(R.id.output); 
      if (mLogShown) { 
       output.setDisplayedChild(1); 
      } else { 
       output.setDisplayedChild(0); 
      } 
      supportInvalidateOptionsMenu(); 
      return true; 
    } 
    return super.onOptionsItemSelected(item); 
} 

/* 
Create a chain of targets that will receive log data 
*/ 
@Override 
public void initializeLogging() { 
    // Wraps Android's native framework 
    LogWrapper logWrapper = new LogWrapper(); 
    // Using Log, front-end to the logging chain, emulates android.util.Log method signatures 
    Log.setLogNode(logWrapper); 

    // Filter strips out everything except the message text 
    MessageOnlyLogFilter filter = new MessageOnlyLogFilter(); 
    logWrapper.setNext(filter); 

    // On screen logging via fragment with a textview 
    LogFragment logFragment = (LogFragment) getSupportFragmentManager() 
      .findFragmentById(R.id.logFragment); 
    filter.setNext(logFragment.getLogView()); 

    Log.i(TAG, "Ready"); 
} 
} 

SwipeRefreshListFragment

public class SwipeRefreshListFragment extends ListFragment { 

private static final String TAG = "SwipeRefreshFragment"; 

// ListFragment where user can swipe up to refresh list 
private SwipeRefreshLayout refreshTaskList; 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
         Bundle savedInstanceState) { 

    // Create list fragment's content view by calling super method 
    final View listFragmentView = super.onCreateView(inflater, container, savedInstanceState); 

    // Now create a SwipeRefreshLayout to wrap the fragment's content view 
    refreshTaskList = new ListFragmentSwipeRefreshLayout(container.getContext()); 

    // Add the list fragment's content view to the SwipeRefreshLayout, making sure that it fills 
    // the SwipeRefreshLayout 
    refreshTaskList.addView(listFragmentView, ViewGroup.LayoutParams.MATCH_PARENT, 
      ViewGroup.LayoutParams.MATCH_PARENT); 

    // Now make sure that the SwipeRefreshLayout fills the Fragment 
    refreshTaskList.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
      ViewGroup.LayoutParams.MATCH_PARENT)); 

    return listFragmentView; 
} 

// Set onRefreshListener to listen for iniated refreshes by user 
public void setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener listener) { 
    refreshTaskList.setOnRefreshListener(listener); 
} 

// Method returns whether or not the SwipeRefreshLayout is refreshing or not 
public boolean isRefreshing() { 
    return refreshTaskList.isRefreshing(); 
} 

// Set whether the SwipeRefreshLayout should be displaying items it is refreshing 
public void setRefreshing(boolean refreshing) { 
    refreshTaskList.setRefreshing(refreshing); 
} 

// Set the color scheme of the Refresh on SwipeRefreshLayout (colors shown at top when refreshing) 
public void setColorScheme(int colorRes1, int colorRes2, int colorRes3, int colorRes4) { 
    refreshTaskList.setColorScheme(colorRes1, colorRes2, colorRes3, colorRes4); 
} 

// Return the Fragment's Widget 
public SwipeRefreshLayout getSwipeRefreshLayout() { 
    return refreshTaskList; 
} 

/* 
Subclass of SwipeRefreshLayout, for use in ListFragment. Needed because SwipeRefreshLayout only supports a single 
child, which it expects to be the one which triggers refreshes. In this case the layout's child is content view 
returned from ListFragment in onCreateView() which is a ViewGroup 

To enable 'swipe-to-refresh' suppoer we need to override the default behavior and properly signal when a gesture is 
possible. This is done by override canChildScrollUp() 
*/ 
private class ListFragmentSwipeRefreshLayout extends SwipeRefreshLayout { 

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

    @Override 
    public boolean canChildScrollUp() { 
     final ListView listView = getListView(); 
     if (listView.getVisibility() == View.VISIBLE) { 
      return canListViewScrollUp(listView); 
     } else { 
      return false; 
     } 
    } 

} 

/* 
Utility method to check whether a ListView can scroll up from it's current position. 
Handles perform version differences, providing backwards compatability where needed. 
*/ 
private static boolean canListViewScrollUp(ListView listView) { 
    if (Build.VERSION.SDK_INT >= 14) { 
     // For IceCream Sandwich and above we can call canScrollVertically() to determine this 
     return ViewCompat.canScrollVertically(listView, -1); // Scroll in the -1 direction 
    } else { 
     // Pre-ICS we need to manually check the first visible item and the child's view top value 
     return listView.getChildCount() > 0 && (listView.getFirstVisiblePosition() > 0 
       || listView.getChildAt(0).getTop() < listView.getPaddingTop()); 
    } 
} 

} 

SwipeRefreshListFragmentFragment

public class SwipeRefreshListFragmentFragment extends SwipeRefreshListFragment { 

private final static String TAG = "TaskListFragment"; 

private static final int LIST_ITEM_COUNT = 20; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    // Notify System to allow options menu for this Fragment 
    setHasOptionsMenu(true); 

} 

@Override 
public void onViewCreated(View view, Bundle savedInstanceState) { 
    super.onViewCreated(view, savedInstanceState); 

    /* 
    Create ArrayAdapter to contain the data for the Listview. Each item in the Listview 
    uses the System defined list_item.xml 
    */ 
    ListAdapter adapter = new ArrayAdapter<String>(
      getActivity(), 
      android.R.layout.simple_list_item_1, 
      Tasks.randomList(LIST_ITEM_COUNT) 
    ); 

    // Set the adapter between ListView and the backing data 
    setListAdapter(adapter); 

    /* 
    Implement SwipeRefreshLayout.OnRefreshListener when users do the 'swipe-to-refresh' 
    gesture, SwipeRefreshLayout invokes onRefresh(). In onRefresh(), call a method that 
    refreshes the content. Call the same method in response to the Refresh action from the 
    action bar. 
    */ 
    setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 
     @Override 
     public void onRefresh() { 
      Log.i(TAG, "onRefresh called from SwipeRefreshLayout"); 
      initiateRefresh(); 
     } 
    }); 
} 

@Override 
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 
    inflater.inflate(R.menu.main_menu, menu); 
} 

/* 
Responds to user's selection of its refresh action item, Start the SwipeRefreshLayout 
progress bar, then initiate the background task that refreshes the content. 

A color scheme menu item used for demonstrating the use of SwipeRefreshLayout's color scheme 
Color scheme should match branding 
*/ 
@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    switch (item.getItemId()) { 
     case R.id.menu_refresh: 
      Log.i(TAG, "Refresh menu item selected"); 

      // We make sure that the SwipeRefreshLayout is displaying its refreshing indicator 
      if (!isRefreshing()) { 
       setRefreshing(true); 
      } 

      // Start our refresh background task 
      initiateRefresh(); 
      return true; 

     case R.id.menu_color_scheme_1: 
      Log.i(TAG, "setColorScheme #1"); 
      item.setChecked(true); 

      // Change the colors displayed by the SwipeRefreshLayout by providing it with 4 
      // color resource Ids 
      setColorScheme(R.color.color_scheme_1_1, R.color.color_scheme_1_2, 
        R.color.color_scheme_1_3, R.color.color_scheme_1_4); 
      return true; 

     case R.id.menu_color_scheme_2: 
      Log.i(TAG, "setColorScheme #2"); 
      item.setChecked(true); 

      // Change the colors displayed by the SwipeRefreshLayout by providing it with 4 
      // color resource ids 
      setColorScheme(R.color.color_scheme_2_1, R.color.color_scheme_2_2, 
        R.color.color_scheme_2_3, R.color.color_scheme_2_4); 
      return true; 

     case R.id.menu_color_scheme_3: 
      Log.i(TAG, "setColorScheme #3"); 
      item.setChecked(true); 

      // Change the colors displayed by the SwipeRefreshLayout by providing it with 4 
      // color resource ids 
      setColorScheme(R.color.color_scheme_3_1, R.color.color_scheme_3_2, 
        R.color.color_scheme_3_3, R.color.color_scheme_3_4); 
      return true; 
    } 

    return super.onOptionsItemSelected(item); 

} 

/* 
By abstracting the refresh process to a single method, the app allows both the SwipeGestureLayout onRefresh() 
method and the Refresh action item to refresh the content 
*/ 
private void initiateRefresh() { 
    Log.i(TAG, "initiateRefresh"); 

    /* 
    Execute the background task, which uses AsyncTask to load data 
    */ 
    new BackgroundTask().execute(); 
} 

/* 
When the asynctask finishes, it finishes onRefreshComplete() which updates the data in the ListAdapter 
and turns off the progress bar 
*/ 
private void onRefreshComplete(List<String> result) { 
    Log.i(TAG, "onRefreshComplete"); 

    // Remove all items from the ListAdapter, and then replace them with new items 
    ArrayAdapter<String> adapter = (ArrayAdapter<String>) getListAdapter(); 
    adapter.clear(); 
    for (String task : result) { 
     adapter.add(task); 
    } 

    // Stop refreshing the indicator 
    setRefreshing(false); 
} 

/* 
AsyncTask which simulates a long running task to fetch new construction tasks 
*/ 
private class BackgroundTask extends AsyncTask<Void, Void, List<String>> { 

    static final int TASK_DURATION = 3 * 1000; // 3 seconds 

    @Override 
    protected List<String> doInBackground(Void... params) { 
     // Sleep for a small amount of time to simulate a background task 
     try { 
      Thread.sleep(TASK_DURATION); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     // Return a new random list of tasks 
     return Tasks.randomList(LIST_ITEM_COUNT); 
    } 

    @Override 
    protected void onPostExecute(List<String> result) { 
     super.onPostExecute(result); 

     // Tell the fragment that the refresh has completed 
     onRefreshComplete(result); 
    } 

} 

} 

ACTIVITYBASE

public class ActivityBase extends FragmentActivity { 

public static final String TAG = "ActivityBase"; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
} 

@Override 
protected void onStart() { 
    super.onStart(); 
    initializeLogging(); 
} 

// Set targets to receive data 
public void initializeLogging() { 
    // Using Log, front-end to the logging chain, emulates android.util.log method signatures 
    // Wraps Android's native log framework 
    LogWrapper logWrapper = new LogWrapper(); 
    Log.setLogNode(logWrapper); 

    Log.i(TAG, "Ready"); 
} 

} 

回答

0

SwipeRefreshLayout只能有一個child.if要添加多個孩子(這是不是一個好的做法,並且應該避免)嘗試進行的LinearLayout和增加孩子的給他們。

相關問題