Activity
があり、それ自体に3つのFragment
があります。
これらのフラグメントの1つには、カスタムアダプタを備えたRecyclerView
があり、そのアイテムの1つをクリックすると、同じActivity
の新しいインスタンスである別のページに移動します。ただし、特定の動作により、アプリでエラーが発生します。
私のアクティビティから、アイテムの1つをクリックすると、同じアクティビティの新しいインスタンスが表示されます。これは問題ありません。次に、戻るボタンを押すと、最初のアクティビティに戻ります。ただし、これらの項目の1つをもう一度クリックすると(同じアクティビティの新しいインスタンスを起動するため)、次のエラーが発生します。
Java.lang.NullPointerException:nullオブジェクト参照で仮想メソッド 'Java.lang.String Android.content.Context.getPackageName()'を呼び出そうとしました
また、アクティビティにあるフラグメントの1つで、アクティビティの新しいインスタンス(つまり、3つのアイテムがある場所)を呼び出していることを考慮することも重要です。だから、私がそれを呼んでいるとき、私は次のようなものを持っています:
_public class MyActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
ViewPager viewPager = (ViewPager) findViewById(R.id.detail_viewpager);
viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
TabLayout tabLayout = (TabLayout) findViewById(R.id.detail_tabs);
tabLayout.setTabTextColors(
ContextCompat.getColor(this, R.color.text_white_secondary),
ContextCompat.getColor(this, R.color.text_white));
tabLayout.setSelectedTabIndicatorColor(ContextCompat.getColor(this, R.color.white));
tabLayout.setupWithViewPager(viewPager);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
}
...
public class ViewPagerAdapter extends FragmentPagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public int getCount() {
return 3;
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0: return new MainFragment();
case 1: return new MyFragment();
case 2: return new MyOtherFragment();
}
return null;
}
@Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.tab_main_frag).toUpperCase(l);
case 1:
return getString(R.string.tab_my_frag).toUpperCase(l);
case 2:
return getString(R.string.tab_my_other_frag).toUpperCase(l);
}
return null;
}
}
...
public static class MyFragment extends Fragment implements MyRVAdapter.OnEntryClickListener {
...
private ArrayList<ItemObj> mArrayList;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
doStuff();
...
}
private void doStuff() {
...
mArrayList = ...;
MyRVAdapter adapter = new MyRVAdapter(getActivity(), mArrayList);
adapter.setOnEntryClickListener(new MyRVAdapter.OnEntryClickListener() {
@Override
public void onEntryClick(View view, int position) {
Intent intent = new Intent(getActivity(), MyActivity.class);
intent.putExtra("INFORMATION", mArrayList.get(position));
startActivity(intent);
}
});
}
...
}
...
}
_
そして、これが私のカスタムアダプタの一部です:
_public class MyRVAdapter extends RecyclerView.Adapter<MyRVAdapter.MyViewHolder> {
public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
...
MyViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
...
}
@Override
public void onClick(View v) {
// The user may not set a click listener for list items, in which case our listener
// will be null, so we need to check for this
if (mOnEntryClickListener != null) {
mOnEntryClickListener.onEntryClick(v, getLayoutPosition());
}
}
}
private Context mContext;
private ArrayList<ItemObj> mArray;
public MyRVAdapter(Context context, ArrayList<ItemObj> array) {
mContext = context;
mArray = array;
}
@Override
public int getItemCount() {
return mArray.size();
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tile_simple, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
ItemObj anItem = mArray.get(position);
...
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
}
private static OnEntryClickListener mOnEntryClickListener;
public interface OnEntryClickListener {
void onEntryClick(View view, int position);
}
public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
mOnEntryClickListener = onEntryClickListener;
}
}
_
完全なエラーは次のとおりです。
_01-23 14:07:59.083 388-388/com.mycompany.myapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mycompany.myapp, PID: 388
Java.lang.NullPointerException: Attempt to invoke virtual method 'Java.lang.String Android.content.Context.getPackageName()' on a null object reference
at Android.content.ComponentName.<init>(ComponentName.Java:77)
at Android.content.Intent.<init>(Intent.Java:4570)
at com.mycompany.myapp.MyActivity$MyFragment$1.onEntryClick(MyActivity.Java:783)
at com.mycompany.myapp.adapter.MyRVAdapter$MyViewHolder.onClick(MyRVAdapter.Java:42)
at Android.view.View.performClick(View.Java:5197)
at Android.view.View$PerformClick.run(View.Java:20926)
at Android.os.Handler.handleCallback(Handler.Java:739)
at Android.os.Handler.dispatchMessage(Handler.Java:95)
at Android.os.Looper.loop(Looper.Java:145)
at Android.app.ActivityThread.main(ActivityThread.Java:5951)
at Java.lang.reflect.Method.invoke(Native Method)
at Java.lang.reflect.Method.invoke(Method.Java:372)
at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:1400)
at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:1195)
_
エラーは最初の行を指しています:Intent intent = new Intent(getActivity(), MyActivity.class);
(フラグメントから)最初に、(エラーの)後の行はオーバーライドされたonClick
メソッドからのmOnEntryClickListener.onEntryClick(v, getLayoutPosition());
を指していますカスタムアダプタ。
私も同様の答えを読みましたが、それらは私の問題を解決していません。
編集:
使用することによって:
_if (getActivity() == null) {
Log.d(LOG_TAG, "Activity context is null");
} else {
Intent intent = new Intent(getActivity(), MyActivity.class);
intent.putExtra("INFORMATION", mArrayList.get(position));
startActivity(intent);
}
_
フラグメントの内部クラス(onEntryClick
)で、getActivity()
を呼び出すとnull
が返されることがわかりました。
だから、問題はこの行です
private static OnEntryClickListener mOnEntryClickListener;
静的であるため、実行時にクラスのインスタンスが1つあります。アイテムをクリックすると、同じアクティビティの2番目のインスタンスが作成され、mOnEntryClickListener
の別のインスタンスも作成され、前のインスタンスが上書きされます。したがって、押し戻してアクティビティの最初のインスタンスに戻ると、破棄された2番目のアクティビティのmOnEntryClickListener
のインスタンスを使用していることになります。
この問題は、匿名のOnClickListener
が元のフラグメントからgetActivity()
メソッドをキャプチャすることに関連している可能性があります。 OnClickListener
にMyFragment
を実装してみて、動作がまったく変わるかどうかを確認できます(それほど単純ではないかもしれません)。
_public static class MyFragment extends Fragment implements View.OnClickListener {
...
private void doStuff() {
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), MyActivity.class);
intent.putExtra("INFORMATION", value);
startActivity(intent);
}
...
}
_
編集:これはハックであり、私が一般的に推奨するものではありませんが、ピンチに陥っている場合は問題を回避できる可能性があります...
Application
を拡張して、静的アプリケーションインスタンスを提供します。
_public class MyApplication extends Application {
private static MyApplication _instance;
@Override
public void onCreate() {
super.onCreate();
_instance = this;
}
public static MyApplication getInstance() {
return _instance;
}
}
_
アプリケーションがMyApplication
を実行するように、AndroidManifest.xmlを変更します。
_<application
Android:name="com.package.MyApplication"
<!-- Rest of your manifest -->
_
次に、getActivity()
をMyApplication.getInstance()
の呼び出しに置き換えます。
_private void doStuff() {
...
mArrayList = ...;
MyRVAdapter adapter = new MyRVAdapter(getActivity(), mArrayList);
adapter.setOnEntryClickListener(new MyRVAdapter.OnEntryClickListener() {
@Override
public void onEntryClick(View view, int position) {
Intent intent = new Intent(MyApplication.getInstance(), MyActivity.class);
intent.putExtra("INFORMATION", mArrayList.get(position));
startActivity(intent);
}
});
}
_
私のソリューションを使用してみてください:MyActivityで、新しい関数を作成します:
public void openAnotherActivity(ObjectItem item) {
Intent intent = new Intent(MyApplication.getInstance(), MyActivity.class);
intent.putExtra("INFORMATION", item);
startActivity(intent);
}
次に、doStuff()を次のように変更します。
private void doStuff() {
...
mArrayList = ...;
MyRVAdapter adapter = new MyRVAdapter(getActivity(), mArrayList);
adapter.setOnEntryClickListener(new MyRVAdapter.OnEntryClickListener() {
@Override
public void onEntryClick(View view, int position) {
openAnotherActivity(mArrayList.get(position))
}
});
}
それがあなたを助けることができるかどうかはわかりませんが、少なくともそれはcontext
問題を解決できると思います
フラグメントのoncreateで、アクティビティの参照を保持し、他のアクティビティを起動する前に、アクティビティが再開された状態にあるかどうかを確認します。
public static class MyFragment extends Fragment implements MyRVAdapter.OnEntryClickListener {
...
private ArrayList<ItemObj> mArrayList;
private MyActivity mActivity;
@Override
public void onCreate(Bundle savedInstanceState) {
mActivity = getActivity();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
doStuff();
...
}
private void doStuff() {
...
mArrayList = ...;
MyRVAdapter adapter = new MyRVAdapter(getActivity(), mArrayList);
adapter.setOnEntryClickListener(new MyRVAdapter.OnEntryClickListener() {
@Override
public void onEntryClick(View view, int position) {
if (!mActivity.isFinishing() {
Intent intent = new Intent(mActivity, MyActivity.class);
intent.putExtra("INFORMATION", mArrayList.get(position));
startActivity(intent);
}
}
});
}
...
}
フラグメントで、タイプYourActivityの変数を作成します。
public MainActivity activity;
次に、アタッチのメソッドを使用して、値を割り当てます。
@Override
public void onAttach(Activity activity){
this.activity = activity;
}
次に、アクティビティをコンテキストとして意図を使用します。
Intent mIntent = new Intent(activity, MyActivity.class);