Reputation: 179
I am having troubles getting Android to set my listener. Somehow the context isn't of the type I am expecting it to be. I'm not sure where I am going wrong.
Below is AddEditCharacterFragment.java, where it is throwing an exception because context isn't of the type I expect.
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 is the activity for the fragment above.
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 which is using an intent to start the AddEditCharacterActivity and subsequently 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();
}
}
And the base class of SingleFragmentActivity for reference:
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();
}
}
}
And 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()));
}
}
}
Upvotes: 2
Views: 1000
Reputation: 27266
Your problem is that you do NOT implement the Interface in your activity…
public class AddEditCharacterActivity extends SingleFragmentActivity {
This does not implement OnCharacterSave
.
This is what I meant with the earlier comment.
UPDATE:
You are misunderstanding Fragments.
// Get the container for the character list
-> This is not true. You're not getting the "Container", you're trying to get an actual instance of a fragment.
InitiativeListFragment initiativeListFragment = (InitiativeListFragment)
fm.findFragmentById(R.id.fragmentContainer);
This would be fine, if that fragment were there.
Let me put it in a more graphic way, this is what you're doing… (give or take)
SingleFragmentActivity
).InitiativeListFragment
in R.id.fragmentContainer
is replaced by AddEditCharacterActivity / AddEditCharacterFragment
combo. R.id.fragmentContainer
contains a fragment of type AddEditCharacterFragment
.OnCharacterSave
, so far so good.onCharacterSave()
which is implemented (see #4), so all is good.R.id.fragmentContainer
and you explicitly say (aka: cast) that the Fragment is of the type InitiativeListFragment
, but… your Activity should know this is not the case… because the current fragment is AddEditCharacterFragment
. What you ought to do is:
It's completely unclear what you're trying to do and it what order because your code doesn't really have a lot of separation of concerns so as you can see your Activities and Fragments are becoming monolithic monsters full of code and business logic. There are solutions and alternatives (Read about Model-View-Presenter or similar patterns) that can ease the mess while providing an easier environment to test your code.
That being said, regardless of the complexity of your code, I believe you need to understand WHY you're getting the exception, and I have the feeling that you need to practice that a little bit.
In short… when you do findFragmentById
, you do get the Fragment (if existing), but you can't just cast it to whatever you want.
OLD COMMENTS:
newInstance()
static methods should generally live inside the Fragments and return new YourFragment();
What I mean is the Fragment creation is usually done via a static method IN the fragment.
Say you have
MyFragment extends Fragment {
public static MyFragment newInstance() {
return new MyFragment();
}
public MyFragment() {
// empty constructor is most of the time needed to restore.
}
}
Then from the activity you usually do what you're doing, but the fragment instance is created by calling MyFragment.newInstance();
(this is how Google does it).
I suggest you add your fragment by Tag as well (it's faster). So you do
final Fragment existing = getSupportFragmentManager().findFragmentByTag(tag);
if (existing == null) {
final Fragment newInstance = MyFragment.newInstance();
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragmentContainer, newInstance, tag)
.commit();
}
Tag is a String
and you can keep it in constants (final static String MYFRAGMENT_TAG = "MYFRAGMENT_TAG";
for example).
Are you using Support.V4 fragments? If so you need to change getFragmentManager()
to getSupportFragmentManager()
(It looks like you are, because you have AppCompatActivity
.
Also, the fragment transaction, should be surrounded by if (savedInstaceState == null) { // do it here }
Upvotes: 2
Reputation: 385
I do not see any problems with creating a fragment instance in your question as stated in other answers.
I think the problem is that your context is AddEditCharacterActivity where you do not implement OnCharacterSave interface.
So you should add:
public class AddEditCharacterActivity extends SingleFragmentActivity implements OnCharacterSave
Upvotes: 0