【问题标题】:Crash on rotation from horizontal to vertical从水平到垂直旋转时崩溃
【发布时间】:2016-06-16 23:24:02
【问题描述】:

我为横向模式和纵向模式使用了两种不同的布局。

花了几个小时后,我无法找出问题所在。 该应用程序在垂直启动时运行良好,在水平旋转时也运行良好。

当我从水平位置旋转回垂直位置并从列表中选择一个项目时,应用程序崩溃。

06-16 14:23:33.769 16108-16108/in.udacity.gagan.cinephilia E/AndroidRuntime: FATAL EXCEPTION: main
Process: in.udacity.gagan.cinephilia, PID: 16108
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.design.widget.CollapsingToolbarLayout.setTitle(java.lang.CharSequence)' on a null object reference
at in.udacity.gagan.cinephilia.DetailActivityFragment$3.onResponse(DetailActivityFragment.java:165)
at in.udacity.gagan.cinephilia.DetailActivityFragment$3.onResponse(DetailActivityFragment.java:148)
at com.android.volley.toolbox.JsonRequest.deliverResponse(JsonRequest.java:65)
at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

主活动回调函数

@Override
public void onItemSelected(String id,
    String backDrop,
    String title,
    int darkColor,
    int lightColor) {

DetailActivityFragment displayFrag = (DetailActivityFragment) getSupportFragmentManager()
                       .findFragmentById(R.id.fragment_left);
    if (displayFrag == null) {
        Intent mIntent = new Intent(MainActivity.this, DetailActivity.class);
                mIntent.putExtra("id", id);
                mIntent.putExtra("backdrop_path", backDrop);
                mIntent.putExtra("title", title);
                mIntent.putExtra("darkColor", darkColor);
                mIntent.putExtra("lightColor", lightColor);
                startActivity(mIntent);
       }
       else 
            displayFrag.getContent(id, lightColor);}

详细活动片段函数

public void getContent(String id, int lightColor) {
        fetchMovieDetails(id, lightColor);
    }

FetchMovieDetails 函数

private void fetchMovieDetails(final String id, final int color){

    mMovieDetail = null;
    mDetailsList.clear();
    String url = TmdbUrls.MOVIE_URL + id + "?" + BuildConfig.THE_MOVIE_DATABASE_API_KEY;
    String reviewURL = TmdbUrls.MOVIE_URL + id + TmdbUrls.REVIEW + "?" + BuildConfig.THE_MOVIE_DATABASE_API_KEY;
    Log.e("TAG", url);

    JsonObjectRequest getDetails = new JsonObjectRequest(url,null, new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            try {
                String imageUrl = "http://image.tmdb.org/t/p/w780/" + response.getString("backdrop_path");
                String iconImageURL="http://image.tmdb.org/t/p/w342/" + response.getString("poster_path");

                String genres = "";
                JSONArray genreArray = response.getJSONArray("genres");

                for (int i = 0; i < genreArray.length(); i++) {
                    String genre = genreArray.getJSONObject(i).getString("name");
                    if (i != genreArray.length() - 1)
                        genres += genre + ", ";
                    else
                        genres += genre + ".";
                }
                mCollapsingToolbarLayout.setTitle(response.getString("title"));
                mMovieDetail = new MovieDetail(id,
                        response.getString("title"),
                        response.getString("vote_average"),
                        genres,
                        response.getString("release_date"),
                        response.getString("status"),
                        response.getString("overview"),
                        iconImageURL,
                        imageUrl,
                        response.getString("tagline"),
                        response.getString("original_language"),
                        response.getString("runtime"),
                        response.getString("popularity"),
                        response.getString("vote_count")
                );
                Glide.with(getActivity())
                        .load(imageUrl)
                        .centerCrop()
                        .crossFade()
                        .diskCacheStrategy(DiskCacheStrategy.RESULT)
                        .into(mImageView);
                //fab.setBackgroundTintList(ColorStateList.valueOf(lightColor));
                mDetailsList.add(mMovieDetail);
                mAdapter = new DetailsAdapter(color, mDetailsList, mReviewList, mTrailersList, getActivity());
                mRecyclerView.setAdapter(mAdapter);

                boolean isMovieInDB = ContentProviderHelperMethods
                        .isMovieInDatabase(getActivity(),
                                String.valueOf(mMovieDetail.getId()));
                if (isMovieInDB) {
                    fab.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_like));
                } else {
                    fab.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_like_outline));
                }

                fab.show();
                fab.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {

                        boolean isMovieInDB = ContentProviderHelperMethods
                                .isMovieInDatabase(getActivity(),
                                        String.valueOf(mMovieDetail.getId()));
                        if (isMovieInDB) {
                            Uri contentUri = MoviesContentProvider.CONTENT_URI;
                            getActivity().getContentResolver().delete(contentUri, "id=?", new String[]{String.valueOf(mMovieDetail.getId())});
                            Snackbar.make(view, getResources().getString(R.string.removed_from_favourites), Snackbar.LENGTH_LONG)
                                    .setAction("Action", null).show();
                            fab.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_like_outline));

                        } else {
                            ContentValues values = new ContentValues();
                            values.put(DatabaseHelper.KEY_ID, mMovieDetail.getId());
                            values.put(DatabaseHelper.KEY_TITLE, mMovieDetail.getTitle());
                            values.put(DatabaseHelper.KEY_RATING, mMovieDetail.getVote_average());
                            values.put(DatabaseHelper.KEY_GENRE, mMovieDetail.getGenre());
                            values.put(DatabaseHelper.KEY_DATE, mMovieDetail.getReleasedate());
                            values.put(DatabaseHelper.KEY_STATUS, mMovieDetail.getStatus());
                            values.put(DatabaseHelper.KEY_OVERVIEW, mMovieDetail.getOverview());
                            values.put(DatabaseHelper.KEY_BACKDROP, mMovieDetail.getBackdrop_path());
                            values.put(DatabaseHelper.KEY_VOTE_COUNT, mMovieDetail.getmVotecount());
                            values.put(DatabaseHelper.KEY_TAG_LINE, mMovieDetail.getmTagline());
                            values.put(DatabaseHelper.KEY_RUN_TIME, mMovieDetail.getmRuntime());
                            values.put(DatabaseHelper.KEY_LANGUAGE, mMovieDetail.getmLanguage());
                            values.put(DatabaseHelper.KEY_POPULARITY, mMovieDetail.getmPopularity());
                            values.put(DatabaseHelper.KEY_POSTER, mMovieDetail.getPoster_path());

                            getActivity().getContentResolver().insert(MoviesContentProvider.CONTENT_URI, values);

                            Snackbar.make(view, getResources().getString(R.string.added_to_favourites), Snackbar.LENGTH_LONG)
                                    .setAction("Action", null).show();

                            fab.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_like));
                        }
                    }
                });
                fetchTrailerData(id);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            showSnackBar(getString(R.string.error_msg));
        }
    });
    NetworkController.getInstance().addToRequestQueue(getDetails);
}

DetailActivityFragment onCreateView

public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);
    View rootView= inflater.inflate(R.layout.fragment_detail, container, false);
    mCollapsingToolbarLayout = (CollapsingToolbarLayout) rootView.findViewById(R.id.collapsing_toolbar_layout_movie_details);

    lightColor = getActivity().getIntent().getIntExtra("lightColor", 0);
    id = getActivity().getIntent().getStringExtra("id");

    mLayoutManager = new LinearLayoutManager(getActivity());
    mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerView);
    mRecyclerView.setLayoutManager(mLayoutManager);
    mRecyclerView.setItemAnimator(new DefaultItemAnimator());


    mToolbar = (Toolbar) rootView.findViewById(R.id.toolbar_movie_details);

    mCollapsingToolbarLayout.setBackgroundColor(lightColor);
    mCollapsingToolbarLayout.setContentScrimColor(lightColor);
    mCollapsingToolbarLayout.setExpandedTitleColor(ContextCompat.getColor(getActivity(), android.R.color.transparent));
    mCollapsingToolbarLayout.setCollapsedTitleTextColor(ContextCompat.getColor(getActivity(), android.R.color.white));

    mImageView = (ImageView) rootView.findViewById(R.id.backdrop);
    fab = (FloatingActionButton) rootView.findViewById(R.id.fab);
    /*fab.setBackgroundTintList(ColorStateList.valueOf(lightColor));*/

    mToolbar.setNavigationIcon(ContextCompat.getDrawable(getActivity(), R.drawable.ic_back));
    mToolbar.inflateMenu(R.menu.menu_detail);
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            getActivity().finish();
        }
    });
    mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
        @Override
        public boolean onMenuItemClick(MenuItem item) {

            if (item.getItemId() == R.id.action_share) {
                startActivity(Intent.createChooser(shareIntent(title), getResources().getString(R.string.share)));
                return true;
            }
            return true;
        }
    });
    fetchMovieDetails(id ,lightColor);
    return rootView;
}

【问题讨论】:

  • 你需要检查mCollapsingToolbarLayout是否为空,当你旋转设备时它会被垃圾回收
  • DetailActivityFragment 包含与从 RecyclerView 中选择的项目相关的详细信息。那么,单击列表项应该再次为其分配内存吧?
  • 如果 DetailActivityFragment 在选择某项时显示是,但您旋转之前的当前任务由于它被收集而不再具有该引用。我会将getContent 移动到片段的 oncreateView 中,这样当您的片段重新创建时,您就会获得真正属于您的问题的信息
  • @GhostCat 我的选择是建议编辑这个问题,因为这个问题还不够,用户只需复制粘贴 xml 并想要答案而不是尝试。我们希望他们编写代码,以便我们可以提供一些改进或正确性。
  • “编辑”表示:任何人都可以通过编辑修复问题(使其可回答)。但是这个问题是 A) 过于宽泛和 B) 非常不清楚(请参阅我给 OP 的 cmets)。因此,“编辑”或“确定”在这里都无效。这个问题需要由 OP 自己 重新处理,因此关闭它(因为太宽泛/不清楚)是唯一有效的选择。我希望这是有道理的。无论如何,我很欣赏快速而善良的回归。

标签: android android-fragments


【解决方案1】:

你真正的问题是你打电话来获取你的信息displayFrag.getContent(id, lightColor); 因为看起来你的片段内容可以改变我不会为此使用静态片段(在你的布局 xml 中声明)并手动添加/替换片段时该项目被点击并发送你的信息片段需要在setArguments()方法中加载

这样,当您的设备旋转时,您仍然拥有信息,并且在您的 onCreateView 中,您只需调用 getContent(id, lightColor) 并且您不应该收到这些错误(尽管在您尝试之前我仍然会检查您的任何视图是否为空使用它们

【讨论】:

  • 好的,我明白了。而不是在xml中使用 我应该使用 FrameLayout 而不是使用 displayFrag.getContent(id, lightColor);我宁愿创建一个 DetailActivityFragment 对象,然后使用 bundle 将值作为参数传递。对吗?
  • 这是正确的,您唯一需要担心的是确保在设备旋转时通过在您的活动 onCreate 中执行类似于DetailActivityFragment displayFrag = (DetailActivityFragment) getSupportFragmentManager() .findFragmentById(R.id.fragment_left); 的操作再次在活动中显示片段。只需确保检查帧是否实际上是片段的实例,否则您将获得强制转换异常
  • 好的,谢谢。我正在实施它。
【解决方案2】:

在 DetailActivityFragment.java 第 165 或 148 行

if(mCollapsingToolbarLayout == null) {
   mCollapsingToolbarLayout = (CollapsingToolbarLayout) rootView.findViewById(R.id.collapsing_toolbar_layout_movie_details);
 } mCollapsingToolbarLayout.setTitle("...")

添加

android:configChanges="orientation|screenSize"

在清单中

第二种方法可能不是满足您需求的最佳解决方案... https://developer.android.com/guide/topics/resources/runtime-changes.html

或者

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

      if(mCollapsingToolbarLayout == null) {
          mCollapsingToolbarLayout = (CollapsingToolbarLayout) 
          getActivity().findViewById(R.id.collapsing_toolbar_layout_movie_details);
    } mCollapsingToolbarLayout.setTitle("...")
 }

【讨论】:

  • rootView 是 onCreateView.function 的局部变量。我将代码从 rootView 更改为 getActivity().findViewById(R.id.collapsing_toolbar_layout_movie_details);它产生了这个错误java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.support.v4.app.FragmentActivity.findViewById(int)' on a null object reference
  • 是的,这是因为此时 Activity 未启动...您可以覆盖 onActivityCreated 然后调用 getActivity().. 检查更新后的答案。
  • 你的第三个建议没有帮助:(我正在尝试你提供的链接。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-24
  • 1970-01-01
  • 1970-01-01
  • 2010-09-30
  • 2015-01-05
  • 2016-10-07
相关资源
最近更新 更多