在充气后访问片段的视图

时间:2014-08-21 19:38:13

标签: android android-fragments nullpointerexception android-inflate

我需要实现的目标

显示ListView的屏幕,如果出现问题(连接丢失,服务器不可用等),可以用错误屏幕替换。 我需要能够在这两个屏幕之间来回切换(编程)。

要求

主屏幕必须是片段 这是因为我的应用程序由几个部分组成,每个部分都可以从导航栏中访问。

到目前为止我做了什么

主要的片段类名为AllQueuesFragment:它的XML布局由FrameLayout组成,我与FragmentManager结合使用以在ErrorFragment之间切换(包含错误消息)和QueuesViewFragment(包含ListView)。

public class AllQueuesFragment extends Fragment
{   
    public AllQueuesFragment()
    {
        super();
    }

    @Override
    public void onStart()
    {
        super.onStart();

        // Show the right fragment based on connectivity status
        checkConnection();
    }

    public void checkConnection()
    {
        final NetworkManager netManager = NetworkManager.getInstance(this.getActivity());

        if (netManager.isConnected())
            showQueues();
        else
            showNoConnection();     
    }

    public void showNoConnection()
    {
        ErrorFragment fragNoConnection = new ErrorFragment();
        displayFragment(fragNoConnection);

        fragNoConnection.setTitle(R.string.text_no_connection);
        fragNoConnection.setIcon(R.drawable.thatfeel);
        fragNoConnection.setLoaderVisibility(false);        
    }

    public void showQueues()
    {
        QueuesViewFragment fragQueuesView = new QueuesViewFragment();
        displayFragment(fragQueuesView);
    }   

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    {
        // Inflate the view
        View rootView = inflater.inflate(R.layout.fragment_allqueues, container, false);

        return rootView;
    }

    // Displays a new fragment
    public void displayFragment(Fragment fragment)
    {       
        if (fragment != null) 
        {
            FragmentManager fragmentManager = getFragmentManager();
            fragmentManager.beginTransaction().replace(R.id.frame_container, fragment).commit();
        } 
    }                                       
}

错误屏幕如下:

public class ErrorFragment extends Fragment 
{   
    private TextView textTitle;

    public ErrorFragment()
    {       
        super();
    }   

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    {
        // Inflate the view
        View rootView = inflater.inflate(R.layout.fragment_error, container, false);

        // Get the widgets
        textTitle = (TextView)rootView.findViewById( R.id.fragment_error_text );

        return rootView;
    }


    // Set methods
    public void setTitle(int id) { textTitle.setText(id); }
}

问题

在布局准备好之前调用setTitle()方法,结果抛出NullPointerException

class AllQueuesFragment
{
    ....

    public void displayFragment(Fragment fragment)
    {       
        if (fragment != null) 
        {
            FragmentManager fragmentManager = getFragmentManager();
            fragmentManager.beginTransaction().replace(R.id.frame_container, fragment).commit();
        } 
    }   

    public void showNoConnection()
    {
        ErrorFragment fragNoConnection = new ErrorFragment();
        displayFragment(fragNoConnection);

        // PROBLEM HERE: Before calling setTitle(), I must be sure that ErrorFragment's 
        // layout is inflated!
        fragNoConnection.setTitle(R.string.text_no_connection);
    }

    ....
}

class ErrorFragment
{
    ....
    public void setTitle(String value) { textTitle.setText(value); }
    ....
}

我无法直接从setTitle()拨打ErrorFragment::onCreateView(),因为我事先并不知道需要显示哪条消息。

如何确保fragNoConnection完成布局?
有没有更好的方法来实现我的目标?

不满意的解决方法

我能想到的唯一解决方法是使用缓冲区来推迟实际调用:

class ErrorFragment
{
    // This string will hold the title until the layout is inflated
    private String titleBuffer;
    private TextView textTitle = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    {
        // Inflate the view
        View rootView = inflater.inflate(R.layout.fragment_error, container, false);

        // Get the widgets
        textTitle = (TextView)rootView.findViewById( R.id.fragment_error_text );

        // Do the actual set
        setTitle(titleBuffer);

        return rootView;
    }

    ....

    public void setTitle(String value) 
    { 
            titleBuffer = value;

            // If the layout is not inflated, defer the actual set
            if (textTitle != null)
                    textTitle.setText(titleBuffer); 
    }
    ....
}

但我不太喜欢这个解决方案(上面的代码已经过简化; ErrorFragment有更多属性)。

通知书?
提前致谢

3 个答案:

答案 0 :(得分:1)

您需要在callback中使用ErrorFragment方法,当视图膨胀时,您可以在onViewCreated的回调界面中调用该方法并设置标题。

样品:

ErroFragment

中的

 public class ErroFragment extends Fragment 
{   
    static interface ErrorDone{
        public void doneInflating();
    }

    private TextView textTitle; 
    private ErrorDone ed;

    public ErroFragment()
    {       
        super();
    }   

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
    {
        // Inflate the view
        View rootView = inflater.inflate(R.layout.fragment_error, container, false);

        // Get the widgets
        textTitle = (TextView)rootView.findViewById( R.id.fragment_error_text );

        return rootView;
    }

    // Set methods
    public void setTitle(int id) { textTitle.setText(id); }
    public void setInterFace(ErrorDone er){ this.ed = er; }
}

然后在AllQueuesFragment

中实施界面
 public class AllQueuesFragment extends Fragment implements ErroFragment.ErrorDone

它将生成方法doneInflating

你需要设置界面:

 public void showNoConnection()
 {
        ErrorFragment fragNoConnection = new ErrorFragment();
        displayFragment(fragNoConnection);
        fragNoConnection.setInterFace(this);      
 }

doneInflating生成的方法(AllQueuesFragment)中,您可以在那里设置标题:

public void doneInflating(){
        fragNoConnection.setTitle(R.string.text_no_connection);
        fragNoConnection.setIcon(R.drawable.thatfeel);
        fragNoConnection.setLoaderVisibility(false); 
}

答案 1 :(得分:1)

这正是应该用于参数的类型:

public void showNoConnection() {
    ErrorFragment fragNoConnection = new ErrorFragment();
    Bundle args = new Bundle();
    //you can also use putInt here if you'd rather pass a string resource id, along with anything else you can stick into a Bundle
    args.putString("title", "some title");
    fragNoConnection.setArguments(args);
    displayFragment(fragNoConnection);
}

然后在ErrorFragment

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

    TextView textTitle = (TextView)rootView.findViewById( R.id.fragment_error_text );
    //now retrieve the argument...
    textTitle.setText(getArguments().getString("title"));

    return rootView;
}

Fragment甚至会在改变方向后记住它的论点。

如果您觉得自己很迂腐,可以在ErrorFragment中创建一个静态工厂方法,将标题作为参数,然后创建Fragment并添加参数,这样就可以实现正确的封装:)< / p>

答案 2 :(得分:0)

如果您想确保FragmentTransaction已提交且有效,您可以使用executePendingTransactions方法:

    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.beginTransaction().replace(R.id.frame_container, fragment).commit();
    fragmentManager.executePendingTransactions();

但是,正确的方法是在实例化时将标题值发送给Fragment。这是从IDE(eclipse或Android Studio)创建片段时的默认模式