为什么onAttach无法识别正确的上下文背景?

时间:2017-02-21 03:31:44

标签: java android android-fragments android-activity

我遇到麻烦让Android设置我的听众。不知何故,上下文不是我期望的类型。我不确定我哪里出错了。

下面是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();
    }
}

使用intent启动AddEditCharacterActivity并随后使用AddEditCharacterFragment的InitiativeTrackerActivity。

    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()));
        }
    }
}

2 个答案:

答案 0 :(得分:2)

您的问题是您没有在活动中实现界面......

public class AddEditCharacterActivity extends SingleFragmentActivity {

implement OnCharacterSave

这就是我对早期评论的意思。

<强>更新

你误解碎片。

// Get the container for the character list - &gt;这不是真的。你没有得到“容器”,你试图得到一个片段的实际实例。

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

如果那个片段存在的话,这没关系。

让我以更加图解的方式表达,这就是你正在做的事情......(给予或接受)

  1. 开始活动XXX(SingleFragmentActivity)。
  2. 在某些时候,InitiativeListFragment中的R.id.fragmentContainerAddEditCharacterActivity / AddEditCharacterFragment组合取代。
  3. 此时,R.id.fragmentContainer包含AddEditCharacterFragment类型的片段。
  4. 由于您的Activity实现了OnCharacterSave,所以到目前为止一直很好。
  5. 仍然在相同的活动/片段组合中,您调用已实现的onCharacterSave()(参见#4),所以一切都很好。
  6. 然后告诉片段管理器在R.id.fragmentContainer中获取片段,并且明确说(也就是:cast)片段属于InitiativeListFragment类型,但是...你的活动应该知道情况并非如此......因为当前的片段是AddEditCharacterFragment
  7. 你应该做的是:

    1. 重新阅读FragmentManager和FragmentTransactions。
    2. 如果您要将信息传递给另一个当前不可见/已启动/附加/等等的片段,那么您必须获得一个参考(如果有的话,可以通过TAG)。< / LI>
    3. 然后,如果可能的话,可以通过Bundle传递“数据”,将其添加到容器中。
    4. 完全不清楚你要做什么以及它的顺序是什么,因为你的代码实际上没有太多的关注点分离,所以你可以看到你的活动和碎片正在成为充满代码和业务逻辑的单片怪物。有一些解决方案和替代方案(阅读模型 - 视图 - 演示者或类似模式)可以减轻混乱,同时提供更简单的测试代码环境。

      话虽如此,无论代码的复杂性如何,我相信你需要明白为什么你会得到异常,我觉得你需要练习一点点。

      简而言之......当你findFragmentById时,你会获得片段(如果存在的话),但你不能把它投射到你想要的任何东西上。

      旧评论

      newInstance()静态方法通常应位于Fragments内并返回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();创建的(这就是谷歌的做法)。

      我建议您按标签添加片段(速度更快)。所以你做了

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

      标记是String,您可以将其保留在常量中(例如final static String MYFRAGMENT_TAG = "MYFRAGMENT_TAG";)。

      您使用的是Support.V4片段吗?如果是这样,您需要将getFragmentManager()更改为getSupportFragmentManager()(看起来就是这样,因为您有AppCompatActivity

      此外,片段事务应该被if (savedInstaceState == null) { // do it here }

      包围

答案 1 :(得分:0)

我没有看到在你的问题中创建片段实例有任何问题,如其他答案中所述。

我认为问题是你的上下文是AddEditCharacterActivity,你没有实现OnCharacterSave接口。

所以你应该添加:

public class AddEditCharacterActivity extends SingleFragmentActivity implements OnCharacterSave