为啥我的 Java 对象在方向更改后引用旧活动?

Posted

技术标签:

【中文标题】为啥我的 Java 对象在方向更改后引用旧活动?【英文标题】:Why does my Java object reference an old activity after orientation change?为什么我的 Java 对象在方向更改后引用旧活动? 【发布时间】:2014-08-13 18:34:05 【问题描述】:

背景:

我有一个使用 DrawerLayout 的 FragmentActivity。我创建了一个名为 NavDrawerManager 的类来抽象出处理抽屉布局的代码。要构造这个对象,我需要传入并保留对 Activity 的引用。我使用此活动引用来调用 findViewById(),并且在创建列表适配器等时也将活动用作上下文。我在活动中保留对 NavDrawerManager 对象的引用,以便我可以对抽屉布局执行回调和操作.

更新:根据 Xaver 的建议,我转而将 FragmentActivity 扩展为 NavDrawerActivity。超类处理导航抽屉代码,而不是在我的活动中使用 NavDrawerManager 对象。查看更新的代码。

问题:

在我改变方向之前一切正常。更改方向后,我的 NavDrawerActivity 似乎仍然引用旧布局。我这样说是因为我在 DrawerLayout 中有一个进度条和几个按钮,我正在尝试更新它们,但它们并不反映更新。

当我调用 Activity.findViewById() 来获取进度条并使其可见时,它不会显示任何更改,也不会显示对按钮的任何更改。注意:Activity.findViewById() 不返回 null。但是,如果我在改变方向之前做同样的事情,进度条和按钮会显示相应的变化。

代码:

NavDrawerActivity:

public class NavDrawerActivity extends LowProfileFragmentActivity 
    private DrawerLayout mDrawerLayout;
    private ActionBarDrawerToggle mDrawerToggle;

    private ExpandableListView mDrawerList;
    private NavDrawerAdapter mListAdapter;

    @Override
    protected void onPostCreate(Bundle savedInstanceState) 
        super.onPostCreate(savedInstanceState);
        mDrawerToggle.syncState();
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        mDrawerToggle.onOptionsItemSelected(item);
        return super.onOptionsItemSelected(item);
    

    //Called via subclass after setContentView    
    protected void setupNavDrawer() 
        initDrawerLayout();
        initDrawerList();
        setButton1();
        setButton2();
    

    private void initDrawerLayout() 
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerToggle = getActionBarDrawerToggle();
        mDrawerLayout.setDrawerListener(mDrawerToggle);
    

    private void initDrawerList() 
        mDrawerList = (ExpandableListView) mDrawerLayout
                .findViewById(android.R.id.list);

        mListAdapter = getNavDrawerAdapter();
    

    private ActionBarDrawerToggle getActionBarDrawerToggle() 
        return new ActionBarDrawerToggle(this, mDrawerLayout,
                R.drawable.ic_navigation_drawer, 0, 0) 

            public void onDrawerClosed(View view) 

            

            public void onDrawerOpened(View view) 

            
        ;
    


    public void setDrawerLoading(boolean loading) 
        ProgressBar progressBar = (ProgressBar) mDrawerLayout
                .findViewById(R.id.progress_bar);
        Button button1 = (Button) this.findViewById(R.id.button1);
        Button button2 = (Button) this
                .findViewById(R.id.button2);

        /* None of the below changes appear after changing orientation */
        if (loading) 
            button1.setEnabled(false);
            button2.setEnabled(false);
            progressBar.setVisibility(View.VISIBLE);
         else 
            progressBar.setVisibility(View.GONE);
            button1.setEnabled(true);
            button2.setEnabled(true);
        
    

    private void setButton1() 
        Button button1 = (Button) this.findViewById(R.id.button1);
        button1.setOnClickListener(new OnClickListener() 

            @Override
            public void onClick(View v) 
                //do something
            

        );
    

    private void setButton2() 
        Button button2 = (Button) this
                .findViewById(R.id.button2);
        button2.setOnClickListener(new OnClickListener() 

            @Override
            public void onClick(View v) 
                //do something
            

        );
    

示例活动:

public class ExampleActivity extends NavDrawerActivity 
    @Override
    public void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        initUI();
    

    private void initUI() 
        setContentView(R.layout.activity_sift);

        // Call the superclass method to set up the nav drawer
        setupNavDrawer();
    

AndroidManifest.xml:

<activity
    android:name="com.example.app.ExampleActivity"
    android:label="@string/app_name"
    android:launchMode="singleTop"
    android:theme="@style/AppTheme" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

【问题讨论】:

如果您实现NavDrawerActivity 而不是NavDrawerManager 不是更容易吗?一般来说,应该避免这样的事情。 UI 应该由FragmentsActivities 自己处理。 这可行。我想我的活动层次结构已经变得相当广泛......我有一个 BaseFragmentActivity > LowProfileFragmentActivity > 然后是这个活动。 (代码中未显示,因为它已被剥离)。我认为制作 LowProfileNavDrawerActivity 有点过头了。 不,如果你创建一个专用的NavDrawerActivity 你应该没问题。 但问题是,我想要一个超类 Activity 来处理使系统 UI 变得“低调”(隐藏通知栏等)。如果我创建一个专用的 NavDrawerActivity,我不能从 LowProfileFragmentActivity 扩展,因为它似乎违反了某种形式的 SRP 对我来说......可能是完全错误的。 不,如果不允许您重用已经存在的实现,那将破坏抽象的目的。很多抽象是很棒的,但是当它过度时,它可能会成为一个问题。不要过分关注完全抽象并符合 OOP 标准的所有内容。 Activity 同时拥有 NavigationDrawer 并隐藏通知栏并没有错。如果你已经有一个Activity 实现了这两件事之一,你应该扩展它。您唯一的其他选择是在第二个Activity 中重新实现相同的行为。 【参考方案1】:

如果您实现NavDrawerActivity 而不是NavDrawerManager,会不会更容易?一般来说,应该避免这样的事情。 UI 应仅由FragmentsActivities 自己处理。

我理解你为什么要创建这样一个NavDrawerManager,只是为了让整个东西可重用并且大量抽象是很棒的,但是当它过度时它可能会成为一个问题。不要过分关注完全抽象并符合 OOP 标准的所有内容。 Activity 同时拥有 NavigationDrawer 并隐藏通知栏没有任何问题。如果你已经有一个 Activity 实现了这两件事之一,你应该扩展它。您唯一的其他选择是在第二个Activity 中重新实现相同的行为,这几乎会破坏抽象的目的。


编辑:我已经重构和改进了你的代码,你真的不应该对每件事都有这么多不同的方法,所有的设置都属于onCreate(),你只需为每个步骤编写额外的方法引入了额外错误的可能性。试试这个:

public abstract class NavDrawerActivity extends LowProfileFragmentActivity 

    private ActionBarDrawerToggle drawerToggle;
    private DrawerLayout drawerLayout;
    private ProgressBar progressBar;
    private Button button1;
    private Button button2;

    private ExpandableListView drawerList;
    private NavDrawerAdapter drawerListAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(getLayout());

        this.drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        this.drawerToggle = new ActionBarDrawerToggle(this, this.drawerLayout, R.drawable.icon_drawer, R.string.drawer_open, R.string.drawer_close);
        this.drawerLayout.setDrawerListener(this.drawerToggle);

        this.drawerList = (ExpandableListView) this.drawerLayout.findViewById(android.R.id.list);
        this.progressBar = (ProgressBar) this.drawerLayout.findViewById(R.id.progress_bar);

        this.button1 = (Button) findViewById(R.id.button1);
        this.button1.setOnClickListener(new View.OnClickListener() 

            @Override
            public void onClick(View v) 
                //do something
            
        );

        this.button2 = (Button) findViewById(R.id.button2);
        this.button2.setOnClickListener(new View.OnClickListener() 

            @Override
            public void onClick(View v) 
                //do something
            
        );

        this.drawerListAdapter = new NavDrawerAdapter(...);
        this.drawerList.setAdapter(this.drawerListAdapter);
    

    protected abstract int getLayout();

    @Override
    protected void onPostCreate(Bundle savedInstanceState) 
        super.onPostCreate(savedInstanceState);
        this.drawerToggle.syncState();
    

    @Override
    public void onConfigurationChanged(Configuration newConfig) 
        super.onConfigurationChanged(newConfig);
        this.drawerToggle.onConfigurationChanged(newConfig);
    

    @Override
    public boolean onOptionsItemSelected(MenuItem item) 
        return this.drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
    

    public void setDrawerLoading(boolean loading) 
        this.button1.setEnabled(!loading);
        this.button2.setEnabled(!loading);
        this.progressBar.setVisibility(loading ? View.VISIBLE : View.GONE);
    

如您所见,我使用了一个名为getLayout() 的抽象方法将正确的布局传递给setContentView()getLayout() 必须由扩展 NavDrawerActivity 的所有 Activities 实现,然后让子 Activities 控制使用的布局。

因此,您的 ExampleActivity 应如下所示:

public class ExampleActivity extends NavDrawerActivity 

    @Override
    protected int getLayout() 
        return R.layout.activity_sift;
    

我测试了一切,它对我有用。

【讨论】:

所以,奇怪的是,我改为创建一个 NavDrawerActivity 并且它仍然表现相同......真的不知道这里发生了什么。 你能更新你的代码吗?我会看看。也请张贴您的清单,我想我知道可能出了什么问题。 好的,我更新了我的代码。让我知道你的想法。 我更新了我的答案。如果您还有其他问题,请随时提出。 我的回答对您有帮助还是还有问题?如果它不适用于我上面发布的代码,那么问题很可能与NavDrawerActivity 无关。

以上是关于为啥我的 Java 对象在方向更改后引用旧活动?的主要内容,如果未能解决你的问题,请参考以下文章

FragmentStatePageAdapter 缓存,重新创​​建活动后如何引用旧片段? (对于前 - 横向模式)

屏幕方向更改后继续活动 - Android

在方向更改后重新创建活动之前更改意图包数据

方向改变后的第二次活动

为啥 saveStateInstance 在屏幕方向上不起作用?

无明显原因的方向更改后应用程序崩溃