2017-02-21 31 views
2

У меня проблемы с Android, чтобы установить слушателя. Так или иначе, контекст не относится к типу, которого я ожидаю. Я не знаю, где я ошибаюсь.Почему onAttach не распознает правильный контекст экземпляра?

Ниже приведена AddEditCharacterFragment.java, где она выдает исключение, потому что контекст не относится к типу, который я ожидаю.

public class AddEditCharacterFragment extends Fragment { 

    public static final String ARG_PARAM1 = "param1"; 

    private InitiativeTrackerDBHelper mHelper; 

    private String mParam1; 

    private Character mCharacter; 

    public AddEditCharacterFragment() { 
     // Required empty public constructor 
    } 

    /** 
    * Use this factory method to create a new instance of 
    * this fragment using the provided parameters. 
    * 
    * @return A new instance of fragment AddEditCharacterFragment. 
    */ 
    // TODO: Rename and change types and number of parameters 
    public static AddEditCharacterFragment newInstance() { 
     AddEditCharacterFragment fragment = new AddEditCharacterFragment(); 
     Bundle args = new Bundle(); 
     //args.putInt(ARG_PARAM1, id); 
     fragment.setArguments(args); 
     return fragment; 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     if (getArguments() != null) { 
      mParam1 = getArguments().getString(ARG_PARAM1); 
     } 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     // Inflate the layout for this fragment 
     View v = inflater.inflate(R.layout.fragment_add_character, container, false); 

     mHelper = new InitiativeTrackerDBHelper(getActivity()); 

     mCharacter = mHelper.addCharacter(); 

     EditText characterNameEditText = (EditText) v.findViewById(R.id.character_name_text_edit); 
     characterNameEditText.setText(mCharacter.getName()); 
     characterNameEditText.addTextChangedListener(new TextWatcher() { 
      public void onTextChanged(CharSequence c, int start, int before, int count) { 
       mCharacter.setName(c.toString()); 
      } 

      public void beforeTextChanged(CharSequence c, int start, int before, int after) { 
      } 

      public void afterTextChanged(Editable c) { 
      } 
     }); 

     EditText modifierPicker = 
       (EditText) v.findViewById(R.id.modEditText); 

     modifierPicker.setText(Integer.toString(mCharacter.getModifier())); 

     modifierPicker.addTextChangedListener(new TextWatcher() { 
      public void onTextChanged(CharSequence c, int start, int before, int count) { 
       mCharacter.setModifier(Integer.parseInt(c.toString())); 
      } 

      public void beforeTextChanged(CharSequence c, int start, int before, int after) { 
      } 

      public void afterTextChanged(Editable c) { 
      } 
     }); 

     Button saveButton = (Button) v.findViewById(R.id.saveButton); 
     saveButton.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       if(mHelper != null) 
       { 
        mHelper.updateCharacter(mCharacter); 
        Toast.makeText(getActivity(), "Update complete!", Toast.LENGTH_LONG).show(); 
        mListener.onCharacterSave(); 
       } 

      } 
     }); 

     return v; 
    } 

    private OnCharacterSave mListener; 

    @Override 
    public void onAttach(Context context) { 
     super.onAttach(context); 
     if (context instanceof OnCharacterSave) { 
      mListener = (OnCharacterSave) context; 
     } else { 
      throw new RuntimeException(context.toString() 
        + " must implement OnFragmentInteractionListener"); 
     } 
    } 

    @Override 
    public void onDetach() { 
     super.onDetach(); 
     mListener = null; 
    } 

    public interface OnCharacterSave { 
     public void onCharacterSave(); 
    } 
} 

AddEditCharacterActivity - это активность для фрагмента выше.

public class AddEditCharacterActivity extends SingleFragmentActivity 
     implements AddEditCharacterFragment.OnCharacterSave { 

    @Override 
    protected Fragment createFragment() { 
     return AddEditCharacterFragment.newInstance(); 
    } 

    @Override 
    public void onCharacterSave() { 
     FragmentManager fm = getFragmentManager(); 

     // Get the container for the character list 
     InitiativeListFragment initiativeListFragment = (InitiativeListFragment) 
       fm.findFragmentById(R.id.fragmentContainer); 

     // Update the UI 
     initiativeListFragment.updateInitiativeList(); 
    } 
} 

InitiativeTrackerActivity, который использует намерение начать AddEditCharacterActivity и впоследствии AddEditCharacterFragment.

public class InitiativeTrackerActivity extends SingleFragmentActivity 
    implements InitiativeListFragment.OnCharacterListListener, AddEditCharacterFragment.OnCharacterSave { 

    @Override 
    protected Fragment createFragment() { 
     return InitiativeListFragment.newInstance(); 
    } 

    @Override 
    public void onAddCharacter() { 
     Intent intent = new Intent(this, AddEditCharacterActivity.class); 
     startActivity(intent); 
    } 

    @Override 
    public void onCharacterSave() { 
     FragmentManager fm = getFragmentManager(); 

     // Get the container for the character list 
     InitiativeListFragment initiativeListFragment = (InitiativeListFragment) 
       fm.findFragmentById(R.id.fragmentContainer); 

     // Update the UI 
     initiativeListFragment.updateInitiativeList(); 
    } 
} 

И базовый класс SingleFragmentActivity для справки:

public abstract class SingleFragmentActivity extends AppCompatActivity { 

    protected abstract Fragment createFragment(); 

    protected int getLayoutId() { 
     return R.layout.activity_single_fragment; 
    } 

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

     FragmentManager fm = getFragmentManager(); 
     Fragment fragment = fm.findFragmentById(R.id.fragmentContainer); 

     if (fragment == null) { 
      fragment = createFragment(); 
      fm.beginTransaction() 
        .add(R.id.fragmentContainer, fragment) 
        .commit(); 
     } 
    } 
} 

И InitiativeListFragment.java

package com.example.twistedpurpose.finalproject; 

import android.content.Context; 
import android.content.Intent; 
import android.database.Cursor; 
import android.net.Uri; 
import android.os.Bundle; 
import android.app.Fragment; 
import android.view.LayoutInflater; 
import android.view.Menu; 
import android.view.MenuInflater; 
import android.view.MenuItem; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.Button; 
import android.widget.CursorAdapter; 
import android.widget.ListView; 
import android.widget.SimpleAdapter; 
import android.widget.TextView; 
import android.widget.Toast; 

import java.lang.reflect.Array; 
import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 


/** 
* A simple {@link Fragment} subclass. 
* Activities that contain this fragment must implement the 
* {@link InitiativeListFragment.OnCharacterListListener} interface 
* to handle interaction events. 
* Use the {@link InitiativeListFragment#newInstance} factory method to 
* create an instance of this fragment. 
*/ 
public class InitiativeListFragment extends Fragment { 

    private InitiativeTrackerDBHelper.CharacterCursor mCursor; 

    private CharacterCursorAdapter adapter; 

    private OnCharacterListListener mListener; 

    public InitiativeListFragment() { 
     // Required empty public constructor 
    } 

    /** 
    * Use this factory method to create a new instance of 
    * this fragment using the provided parameters. 
    * 
    * @return A new instance of fragment InitiativeListFragment. 
    */ 
    public static InitiativeListFragment newInstance() { 
     return new InitiativeListFragment(); 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     if (adapter != null) { 
      adapter.notifyDataSetChanged(); 
     } 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
          Bundle savedInstanceState) { 
     // Inflate the layout for this fragment 
     View v = inflater.inflate(R.layout.fragment_initiative_list, container, false); 

     //getActivity().deleteDatabase("characters.db"); 

     Context context = getActivity(); 

     // 1. Create a new InitiativeTrackerDBHelper 
     InitiativeTrackerDBHelper dbHelper = new InitiativeTrackerDBHelper(context); 


     // 2. Query the characters and obtain a cursor (store in mCursor). 
     mCursor = dbHelper.queryCharacters(); 

     // Find ListView to populate 
     ListView characterListView = (ListView) v.findViewById(R.id.character_listView); 
     // Setup cursor adapter using cursor from last step 
     adapter = new CharacterCursorAdapter(context, mCursor); 
     // Attach cursor adapter to the ListView 
     characterListView.setAdapter(adapter); 

     Button rollButton = (Button) v.findViewById(R.id.rollBtn); 
     rollButton.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       InitiativeTrackerDBHelper dbHelper = new InitiativeTrackerDBHelper(getContext()); 
       List<Character> characterList = dbHelper.getCharacters(); 

       InitiativeRoller.rollInitiative(characterList); 

       for (Character c : characterList) { 
        dbHelper.updateCharacter(c); 
       } 

       updateInitiativeList(); 
       Toast.makeText(getContext(), "Roll initiative!", Toast.LENGTH_SHORT).show(); 
      } 
     }); 

     Button addButton = (Button) v.findViewById(R.id.addBtn); 
     addButton.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       if(mListener != null) { 
        mListener.onAddCharacter(); 
       } 
      } 
     }); 

     return v; 
    } 

    public void updateInitiativeList(){ 
     if(mCursor != null && adapter != null){ 
      mCursor.requery(); 
      adapter.notifyDataSetChanged(); 
     } 

    } 

    @Override 
    public void onAttach(Context context) { 
     super.onAttach(context); 
     if (context instanceof OnCharacterListListener) { 
      mListener = (OnCharacterListListener) context; 
     } else { 
      throw new RuntimeException(context.toString() 
        + " must implement OnFragmentInteractionListener"); 
     } 
    } 

    @Override 
    public void onDetach() { 
     super.onDetach(); 
     mListener = null; 
    } 

    /** 
    * This interface must be implemented by activities that contain this 
    * fragment to allow an interaction in this fragment to be communicated 
    * to the activity and potentially other fragments contained in that 
    * activity. 
    * <p> 
    * See the Android Training lesson <a href= 
    * "http://developer.android.com/training/basics/fragments/communicating.html" 
    * >Communicating with Other Fragments</a> for more information. 
    */ 
    public interface OnCharacterListListener { 
     public void onAddCharacter(); 
    } 

    /** 
    * A character cursor adaptor for adding characters 
    * to a list 
    */ 
    private static class CharacterCursorAdapter extends CursorAdapter { 

     private InitiativeTrackerDBHelper.CharacterCursor mCharacterCursor; 

     public CharacterCursorAdapter(Context context, InitiativeTrackerDBHelper.CharacterCursor cursor) { 
      super(context, cursor, 0); 
      mCharacterCursor = cursor; 
     } 

     @Override 
     public View newView(Context context, Cursor cursor, ViewGroup parent) { 
      // Use a layout inflater to get a row view 
      LayoutInflater inflater = (LayoutInflater) context 
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      return inflater.inflate(R.layout.character_listview, parent, false); 
     } 

     @Override 
     public void bindView(View view, Context context, Cursor cursor) { 
      TextView characterName = (TextView) view.findViewById(R.id.name); 
      TextView characterMod = (TextView) view.findViewById(R.id.mod); 
      TextView characterInit = (TextView) view.findViewById(R.id.init); 

      characterName.setText(mCharacterCursor.getCharacter().getName()); 
      characterMod.setText(Integer.toString(mCharacterCursor.getCharacter().getModifier())); 
      characterInit.setText(Integer.toString(mCharacterCursor.getCharacter().getTotalInitiative())); 
     } 
    } 
} 
+0

чем проблема? –

+0

В onAttach вместо создателя mListener он попадает в другое и выдает исключение. –

+0

Это неправильный способ общения с вашей деятельностью, вы можете найти подходящий способ на https://developer.android.com/training/basics/fragments/communicating.html. В вашем коде вам нужно использовать context.getActivity(). которые дают вам тип экземпляра, если OnCharacterSave, если вы хотите перейти с текущей реализацией. – Avi

ответ

1

Ваша проблема заключается в том, что вы не реализуете интерфейса в вашей деятельности ...

public class AddEditCharacterActivity extends SingleFragmentActivity {

неimplement OnCharacterSave.

Это то, что я имел в виду с более ранним комментарием.

UPDATE:

Вы недопонимание Фрагменты.

// Get the container for the character list -> Это неправда. Вы не получаете «Контейнер», вы пытаетесь получить фактический экземпляр фрагмента.

InitiativeListFragment initiativeListFragment = (InitiativeListFragment) 
      fm.findFragmentById(R.id.fragmentContainer); 

Это было бы хорошо, если бы этот фрагмент был там.

Позвольте мне поставить его в более наглядной форме, это то, что вы делаете ... (давать или принимать)

  1. Start активность XXX (SingleFragmentActivity).
  2. В какой-то момент InitiativeListFragment в R.id.fragmentContainer заменяется на AddEditCharacterActivity/AddEditCharacterFragment комбо.
  3. На данный момент R.id.fragmentContainer содержит фрагмент типа AddEditCharacterFragment.
  4. Поскольку ваша деятельность реализует OnCharacterSave, пока все хорошо.
  5. В этом же упражнении/фрагментации вы вызываете onCharacterSave(), который реализован (см. № 4), поэтому все хорошо.
  6. Вы тогда скажите менеджеру Fragment, чтобы получить Вас фрагмент в R.id.fragmentContainer и вы явно говорят (иначе: литой), что фрагмент типа InitiativeListFragment, но ... ваша активность должна знать, что это не так ... потому что текущий фрагмент AddEditCharacterFragment.

То, что вы должны сделать, это:

  1. перечитать о FragmentManager и FragmentTransactions.
  2. Если вы собираетесь передать информацию в другой фрагмент , который в настоящее время не виден/запущен/приложен/etc., тогда вам нужно получить ссылку (через TAG, если она у вас есть).
  3. Тогда, возможно, добавьте его в контейнер, если это возможно, передавая «данные» через Bundle.

Это совершенно непонятно, что вы пытаетесь сделать, и это то, что порядок, потому что ваш код не очень много разделения проблем, так как вы можете видеть ваши действия и фрагменты становятся монолитными монстрами полные кода и бизнес-логики. Существуют решения и альтернативы (читайте о Model-View-Presenter или аналогичных шаблонах), которые могут облегчить беспорядок, обеспечивая при этом более легкую среду для проверки вашего кода.

Учитывая, что, независимо от сложности вашего кода, я считаю, что вам нужно понять, ПОЧЕМУ вы получаете исключение, и у меня возникает ощущение, что вам нужно немного попрактиковаться в этом.

Короче говоря ... когда вы делаете findFragmentById, вы получаете Фрагмент (если он существует), но вы не можете просто отбросить его на все, что захотите.

OLD КОММЕНТАРИИ:

newInstance() статические методы обычно должны жить внутри фрагментов и возвращающие new YourFragment();

То, что я имею в виду создание фрагмента обычно делается с помощью статического метода в фрагменте.

Скажем, у вас есть

MyFragment extends Fragment { 
    public static MyFragment newInstance() { 
     return new MyFragment(); 
    } 

    public MyFragment() { 
      // empty constructor is most of the time needed to restore. 
    } 
} 

Тогда от активности вы обычно делаете, что вы делаете, но экземпляр фрагмента создается вызовом MyFragment.newInstance(); (это, как Google делает это).

Я предлагаю вам добавить свой фрагмент с помощью Tag также (это быстрее). Таким образом, вы

final Fragment existing = getSupportFragmentManager().findFragmentByTag(tag); 

if (existing == null) { 
    final Fragment newInstance = MyFragment.newInstance(); 
     getSupportFragmentManager() 
      .beginTransaction() 
      .add(R.id.fragmentContainer, newInstance, tag) 
      .commit(); 
} 

Tag является String и вы можете сохранить его в постоянных (final static String MYFRAGMENT_TAG = "MYFRAGMENT_TAG";, например).

Вы используете фрагменты Support.V4? В этом случае вы должны изменить getFragmentManager() к getSupportFragmentManager() (Похоже, что вы есть, потому что у вас есть AppCompatActivity.

Кроме того, сделка фрагмент, должен быть окружен if (savedInstaceState == null) { // do it here }

+0

Разве это не реализовано в InitiativeTrackerActivity? Кроме того, можете ли вы подробнее рассказать о своей второй точке? Я не совсем понимаю. –

+0

Вы прикрепляете фрагмент 'AddEditCharacterFragment' к' AddEditCharacterActivity', а не 'InitiateTrackerActivity', поэтому тот, который должен его реализовать, это' AddEdit ... ' –

+0

Похоже, что он работает лучше (на самом деле он нажимает onCharacterSave), но бросая исключение при кастинге в InitiativeListFragment при попытке получить фрагмент. Получил бы фрагмент с помощью тега? Должен ли я отделить создание фрагмента от SingleFragmentActivity для установки тегов или каким-то образом позволить ему установить тег? –

0

Я не вижу никаких проблем с созданием фрагмента в вашем ответе, как указано в других ответах.

Я думаю, проблема заключается в том, что ваш контекст - это AddEditCharacterActivity, где вы не реализуете интерфейс OnCharacterSave.

Таким образом, вы должны добавить:

public class AddEditCharacterActivity extends SingleFragmentActivity implements OnCharacterSave 
+0

Это похоже на трюк, но теперь фрагмент, который я возвращаю из диспетчера фрагментов, не является инициативнымListFragment, поэтому он генерирует ошибку, когда я пытаюсь применить ее к этому типу. Любые мысли о том, что я могу там сделать? Использует ли теги это решение или сделал ли мой фрагмент собранным мусором? Должен ли это быть новым вопросом? –