アプリには、3つのフラグメントを含むFragmentPagerAdapter
を持つ ViewPager があります。これらのフラグメントの2つは、それぞれの Recyclerview で構成されています。
最初のページ(ViewPagerのないフラグメント)が正しく表示されます。ただし、ViewPagerが次のページ(RecyclerView)をプリロードしようとすると、次のログを含むNullPointerException
が原因で、アプリがクラッシュします。
Java.lang.NullPointerException: Attempt to invoke virtual method 'boolean Android.support.v7.widget.RecyclerView$ViewHolder.shouldIgnore()' on a null object reference
at Android.support.v7.widget.RecyclerView.findMinMaxChildLayoutPositions(RecyclerView.Java:2839)
at Android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.Java:2626)
at Android.support.v7.widget.RecyclerView.onLayout(RecyclerView.Java:3011)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.support.v4.view.ViewPager.onLayout(ViewPager.Java:1626)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.widget.FrameLayout.layoutChildren(FrameLayout.Java:573)
at Android.widget.FrameLayout.onLayout(FrameLayout.Java:508)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.widget.LinearLayout.setChildFrame(LinearLayout.Java:1703)
at Android.widget.LinearLayout.layoutVertical(LinearLayout.Java:1557)
at Android.widget.LinearLayout.onLayout(LinearLayout.Java:1466)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.support.design.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.Java:1000)
at Android.support.design.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.Java:710)
at Android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.Java:724)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.support.v4.widget.DrawerLayout.onLayout(DrawerLayout.Java:907)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.widget.FrameLayout.layoutChildren(FrameLayout.Java:573)
at Android.widget.FrameLayout.onLayout(FrameLayout.Java:508)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.widget.LinearLayout.setChildFrame(LinearLayout.Java:1703)
at Android.widget.LinearLayout.layoutVertical(LinearLayout.Java:1557)
at Android.widget.LinearLayout.onLayout(LinearLayout.Java:1466)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.widget.FrameLayout.layoutChildren(FrameLayout.Java:573)
at Android.widget.FrameLayout.onLayout(FrameLayout.Java:508)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.widget.LinearLayout.setChildFrame(LinearLayout.Java:1703)
at Android.widget.LinearLayout.layoutVertical(LinearLayout.Java:1557)
at Android.widget.LinearLayout.onLayout(LinearLayout.Java:1466)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.widget.FrameLayout.layoutChildren(FrameLayout.Java:573)
at Android.widget.FrameLayout.onLayout(FrameLayout.Java:508)
at Android.view.View.layout(View.Java:15684)
at Android.view.ViewGroup.layout(ViewGroup.Java:4981)
at Android.view.ViewRootImpl.performLayout(ViewRootImpl.Java:2186)
at Android.view.ViewRootImpl.performTraversals(ViewRootImpl.Java:1920)
at Android.view.ViewRootImpl.doTraversal(ViewRootImpl.Java:1106)
at Android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.Java:6018)
at Android.view.Choreographer$CallbackRecord.run(Choreographer.Java:792)
at Android.view.Choreographer.doCallbacks(Choreographer.Java:596)
at Android.view.Choreographer.doFrame(Choreographer.Java:557)
at Android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.Java:778)
at Android.os.Handler.handleCallback(Handler.Java:739)
at Android.os.Handler.dispatchMessage(Handler.Java:95)
at Android.os.Looper.loop(Looper.Java:155)
at Android.app.ActivityThread.main(ActivityThread.Java:5696)
at Java.lang.reflect.Method.invoke(Native Method)
at Java.lang.reflect.Method.invoke(Method.Java:372)
次に、ViewPagerの宣言方法を示します。
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
adapter.addFrag(fragment1, "fragment1");
adapter.addFrag(fragment2, "fragment2");
adapter.addFrag(fragment3, "fragment3");
viewPager.setAdapter(adapter);
そしてアダプター:
private class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
public void addFrag(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
RecyclerViewの両方からのコードは長く、ページごとに異なるので、どの部分が関連しているかわからないので、サンプルは提供しません。問題の修正に役立つと思われる場合は、遠慮なく特定の部分をお尋ねください。
私が言えることの1つは、それを機能させたい場合は、両方のRecylerViewからの各setAdapter
の呼び出しにコメントする必要があるということです。
[〜#〜] edit [〜#〜]:2ページ目のコードは次のとおりです。
public class MyFragment extends Fragment {
RecyclerView recyclerView;
GridAdapter gridAdapter;
public GridAdapter getGridAdapter() {
return gridAdapter;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.our_layout, container, false);
recyclerView = (RecyclerView) v.findViewById(R.id.recycler_view);
gridLayoutManager.setSmoothScrollbarEnabled(true);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.setHasFixedSize(true);
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ArrayList<Model> model = getArguments().getParcelableArrayList("extra");
if (model != null && model.size() != 0) {
gridAdapter = new GridAdapter(model);
recyclerView.setAdapter(gridAdapter);
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && isResumed()){
onResume();
}
}
@Override
public void onResume() {
super.onResume();
if (!getUserVisibleHint())
return;
}
public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
private int spanCount;
private int spacingLeft;
private int spacingRight;
private int spacingTop;
private int spacingBottom;
private boolean includeEdge;
public GridSpacingItemDecoration(int spanCount, int spacingLeft, int spacingTop, int spacingRight, int spacingBottom, boolean includeEdge) {
this.spanCount = spanCount;
this.spacingLeft = spacingLeft;
this.spacingRight = spacingRight;
this.spacingTop = spacingTop;
this.spacingBottom = spacingBottom;
this.includeEdge = includeEdge;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
if (includeEdge) {
outRect.left = spacingLeft - column * spacingLeft / spanCount; // spacing - column * ((1f / spanCount) * spacing)
outRect.right = (column + 1) * spacingRight / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
if (position < spanCount) { // top Edge
outRect.top = spacingTop;
}
outRect.bottom = spacingBottom; // item bottom
} else {
outRect.left = column * spacingLeft / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacingRight - (column + 1) * spacingRight / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacingTop; // item top
}
}
}
}
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {
private ArrayList<Model> model;
public GridAdapter(ArrayList<Model> offer) {
super();
model = offer;
}
@Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item, parent, false);
final ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final Model currentOffer = model.get(position);
holder.category.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
int width = holder.category.getWidth();
ViewGroup.LayoutParams params = holder.appIcon.getLayoutParams();
params.width = width;
params.height = width;
holder.appIcon.setLayoutParams(params);
if (Android.os.Build.VERSION.SDK_INT >= Android.os.Build.VERSION_CODES.JELLY_BEAN)
holder.itemView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
else
holder.itemView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
});
Picasso.with(getActivity().getApplicationContext()).
load(currentOffer.getApp_logo()).fit().centerCrop().into(holder.appIcon);
holder.appName.setText(currentOffer.getApp_name());
holder.category.setText(currentOffer.getApp_category());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String marketURL = AndroidTools.getPlayStoreURL(currentOffer.getApp_store_id(), true);
UITools.launchUrl(getActivity(), marketURL);
}
});
}
@Override
public int getItemCount() {
return model.size();
}
class ViewHolder extends RecyclerView.ViewHolder {
private ImageView appIcon;
private TextView appName;
private TextView category;
public ViewHolder(View itemView) {
super(itemView);
appIcon = (ImageView)itemView.findViewById(R.id.item_icon);
appName = (TextView)itemView.findViewById(R.id.item_app_name);
category = (TextView)itemView.findViewById(R.id.item_category);
}
}
}
}
どんな助けでも大歓迎です。
私の開発の1つ中にこのエラーが発生しました。 XMLファイルのRecyclerViewがFrameLayoutのような別のレイアウトに正しくラップされていることを確認しましたか?
そうでない場合は、Viewpagerでのみクラッシュし、単一のフラグメントビューではクラッシュしません。
これは、誤ってビューをRecyclerView
に直接追加した場合に発生します。私の場合、View.inflate
RecyclerView
を親パラメータとして使用するデコレータレイアウトの場合、自動的にアタッチされます。 RecyclerView
は、それに接続されているすべての子を反復処理し、そのすべてのビューの子がレイアウトパラメータにViewHolders
を持っていることを期待し、子のビューホルダーがnullの場合にこのNPEをスローします。
リサイクラービューに子なしを追加し、inflate()
メソッドの3番目のパラメーターであるattachToRoot
をfalse
whileに設定します。カスタムレイアウトを膨らませることでうまくいきました。
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.online_user, parent, false);
return new RecyclerViewHolder(view.findViewById(R.id.onlineUserView));
}
レイアウト:
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:orientation="vertical">
<Android.support.v7.widget.RecyclerView
Android:id="@+id/onlineUsersView"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
</LinearLayout>
これは、xmlレイアウトファイルに要素をlistView
またはRecyclerView
のすぐ下に追加すると発生します。
<Android.support.v7.widget.RecyclerView
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:scrollbars="vertical">
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content" />
</Android.support.v7.widget.RecyclerView>
ここで、RecyclerView内にTextViewを追加しました。これにより、onLayout error
(NullPointerException
が原因)がスローされます。RecyclerView
またはlistView
の直下に要素を追加しないでください。