【问题标题】:Android fragment onCreateView delayedAndroid片段onCreateView延迟
【发布时间】:2019-12-22 19:51:42
【问题描述】:

背景和问题:

我有一个自定义的Fragment 类。我将这个片段类重用于多个状态显示。在父Activity 中,我加载片段并调用片段上的方法来设置一些文本等。当我这样做时,我点击了NullPointerException。原因是onCreateView()还没有被调用,即视图不存在,因此访问TextView调用.setText(String)当然会遇到NullPointerException

片段类

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import com.example.app.R;

public class CopyStaticFragment extends Fragment {

    private String initialActionText, initialSecondaryText, initialTitleText;
    private TextView textViewTitle, textViewSecondary;
    private Button btnAction;
    private View.OnClickListener actionCallback;

    public void setActionButtonListener(View.OnClickListener actionCallback) {
        this.actionCallback = actionCallback;
    }

    public CopyStaticFragment() {
    }

    public CopyStaticFragment(View.OnClickListener actionCallback) {
        this.actionCallback = actionCallback;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View layout = inflater.inflate(R.layout.fragment_main_status, container, false);

        textViewTitle = layout.findViewById(R.id.textViewTitle);
        textViewTitle.setText(initialTitleText);
        textViewSecondary = layout.findViewById(R.id.textViewSecondary);
        textViewSecondary.setText(initialSecondaryText);
        btnAction = layout.findViewById(R.id.btnAction);
        btnAction.setText(initialActionText);
        btnAction.setOnClickListener(v -> {
            if(actionCallback != null) {
                actionCallback.onClick(v);
            }
        });

        return layout;
    }

    public void setPrimaryText(String text) {
        textViewTitle.setText(text);
    }

    public void setSecondaryText(String text) {
        textViewSecondary.setText(text);
    }

    public void setActionText(String text) {
        btnAction.setText(text);
    }

    public void setInitialActionText(String text) {
        this.initialActionText = text;
    }

    public void setInitialSecondaryText(String text) {
        this.initialSecondaryText = text;
    }

    public void setInitialTitleText(String text) {
        this.initialTitleText = text;
    }

}

在我的主要活动中,作为来自Service 状态的回调响应,我将执行以下操作:

if (instance.getUnbackedUpCount() > 0) {
    // Load first fragment - i.e. we can backup content
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.frameCurrentStatus, copyStartFragment)
            .commit();
    capacityControlFragment.setInfoIconVisible(true);
} else {
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.frameCurrentStatus, copyFinishFragment)
            .commit();
}

其中copyStartFragment & copyFinishFragment 是上述片段的实例。

注意:

根据Service 状态更新间隔,对replace() 片段的调用几乎是瞬时的——我认为这是实际问题。

我的解决方法:

为所有视图及其属性设置 initial 设置器。例如initialTitleTextinitialMessageTextinitialActionButtonTextinitialActionButtonVisibility 等。我觉得这是 hack'ish,不符合 Fragment 使用的预期设计理念。

我的问题

这是我遇到此类错误的设计问题,还是有解决方法可以尽快致电onCreateView()

【问题讨论】:

  • public CopyStaticFragment(View.OnClickListener actionCallback) { 除非您还使用了 FragmentFactory,但您可能没有使用,否则您应该会收到一个 lint 错误,如果您这样做,您的应用程序将以可怕的方式崩溃。你所有的set* 方法都应该通过Bundle arguments 使用fragment.setArguments(Bundle) 传递。
  • @EpicPandaForce 感谢您的建议。 set* 方法只是作为一种临时措施——如果没有给出主要问题的解决方案,将实施捆绑。另外,请您解释一下构造函数中的回调问题吗?
  • @EpicPandaForce 非常有帮助,谢谢。此外,您在上面的评论中提到的解决方案似乎也包括我正在使用的这种“解决方法”。即事先设置这些值 - 所以onCreateView() 延迟似乎是一个实际问题。
  • 可以在onViewCreated中设置Text。

标签: java android android-fragments android-lifecycle


【解决方案1】:

您正在使用 .commit() 作为替换片段的一部分。
这是异步完成的。没办法,这就是那个函数的工作原理。

您可以使用.commitNow(),它会同步完成。
通过这样做,您应该能够在函数返回后立即访问视图。

https://developer.android.com/reference/androidx/fragment/app/FragmentTransaction#commitNow()

【讨论】:

  • 啊,是的,commitNow()。如果片段管理器还有其他待处理的事务,似乎不建议这样做。我宁愿在问题评论部分选择@EpicPandaForce 的解决方案作为更安全的选择。
【解决方案2】:

使用推荐的方式将初始字符串传递给片段以初始化片段:

public static CopyStaticFragment newInstance(String initialString....) {
    CopyStaticFragment fragment = new CopyStaticFragment();
    Bundle args = new Bundle();
    args.putString(INITIAL_STRING, initialString);

    fragment.setArguments(args);
    return fragment;
}

然后在onCreate:

 @Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        mInitialString = getArguments().getString(INITIAL_STRING);
    }
}

然后,当您在OnViewCreated 中设置TextView 文本时,字符串和任何其他参数都可用。

使用以下方法在活动中初始化您的片段:

CopyStaticFragment.newInstance("initial string",....);

附注:onCreateView 只能用于扩充您的布局资源,使用OnViewCreated 在布局扩充后初始化您的视图。

尽量远离commitNow()popBackStackImmediate(),除非你喜欢IllegalStateException

【讨论】:

    猜你喜欢
    • 2011-12-23
    • 2016-06-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多