在操作栏中显示/隐藏项目 Android (3.0+)

发布于 2024-12-28 18:02:20 字数 9883 浏览 0 评论 0 原文

我正在使用内置操作栏。 我想在获取数据时在操作栏中显示一个动画加载程序,并在不获取数据时显示“刷新”图标。我发现的简单方法是使用 2 个菜单项并显示一个/隐藏另一个,然后显示相反的菜单项。这是我的菜单:

<item android:id="@+id/menuItemRefresh" 
      android:title="Refresh" 
      android:showAsAction="always" 
      android:icon="@drawable/ic_menu_refresh"
      android:visible="false" />
<item android:id="@+id/menuItemProgress"
      android:actionLayout="@layout/progress"
      android:showAsAction="always"
      android:visible="true" />

我正在使用 actionLayout (如您所见),因为我不知道如何在菜单中创建不确定的进度条旋转。这是相应的actionLayout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/layoutProgress" >

    <ProgressBar
        android:id="@+id/menuItemRefresh"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_marginLeft="13dp"
        android:layout_marginRight="13dp" />

</LinearLayout>

我的活动:

public class MainActivity extends Activity implements OnItemClickListener
{
    Handler             handler;
    MenuItem            menuItemProgress;
    MenuItem            menuItemRefresh;

    int                 currentPage;
    List<CloudAppItem>  items;
    boolean             currentlyLoading    = false;
    ListView            listView;
    FilesAdapter        filesAdapter;
    LinearLayout        emptyView;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        Log.i("MainActivity", "Started");

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);
        setHandler();
        setListing();

        loadItems();
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        if (isFirstRun() && haveCredentialsChanged())
        {
            setFirstRun(false);
        }

        if (isFirstRun())
        {
            firstRun();
        }
        else if (haveCredentialsChanged())
        {
            setCredentialsChanged(false);
            loadFiles(true, 0);
        }
        else
        {
            loadFiles(false, 0);
        }
    }

    @Override
    public Object onRetainNonConfigurationInstance()
    {
        return items;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {               
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);

        this.menuItemProgress = menu.findItem(R.id.menuItemProgress);
        this.menuItemRefresh = menu.findItem(R.id.menuItemRefresh);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch (item.getItemId())
        {
            case R.id.menuItemPreferences:

                startActivity(new Intent(MainActivity.this, PreferencesActivity.class));

            break;

            case R.id.menuItemRefresh:

                loadFiles(true, 0);

            break;
        }

        return false;
    }

    private boolean isFirstRun()
    {
        return Prefs.getPreferences(this).getBoolean(Prefs.FIRST_RUN, true);
    }

    private void firstRun()
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("First run");
        builder.setCancelable(false);
        builder.setPositiveButton("ok", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which)
            {
                startActivity(new Intent(MainActivity.this, PreferencesActivity.class));
            }
        });

        AlertDialog alert = builder.create();
        alert.show();
    }

    private void setFirstRun(boolean b)
    {
        Prefs.getPreferences(this).edit().putBoolean(Prefs.FIRST_RUN, b).commit();
    }

    private boolean haveCredentialsChanged()
    {
        return Prefs.getPreferences(this).getBoolean(Prefs.CREDENTIALS_CHANGED, false);
    }

    private void setCredentialsChanged(boolean b)
    {
        Prefs.getPreferences(this).edit().putBoolean(Prefs.CREDENTIALS_CHANGED, b).commit();
    }

    private void setListing()
    {
        listView = (ListView) findViewById(R.id.listView);
        emptyView = (LinearLayout) findViewById(R.id.emptyView);
        listView.setOnItemClickListener(this);
        listView.setEmptyView(emptyView);
    }

    @SuppressWarnings("unchecked")
    private void loadItems()
    {
        Object obj = getLastNonConfigurationInstance();
        if (obj != null)
        {
            items = (List<CloudAppItem>) obj;
            MainActivity.this.handler.sendEmptyMessage(0);
        }
        else
        {
            if (!isFirstRun())
                loadFiles(true, 0);
        }
    }

    private void showProgressIcon(boolean show)
    {
        if(this.menuItemProgress != null && this.menuItemRefresh != null)
        {                       
            if(show)
            {               
                this.menuItemProgress.setVisible(true); 
                this.menuItemRefresh.setVisible(false);                         
            }
            else
            {
                this.menuItemRefresh.setVisible(true);

                // PROBLEM : LINE TRIGGERING ERROR
                this.menuItemProgress.setVisible(false);    
            }
        }
    }

    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
    {
        if(this.items.get(arg2).isFake())
        {
            filesAdapter.lastItemLoading = true;
            filesAdapter.notifyDataSetChanged();

            int page = (int)(this.items.size()/Integer.valueOf(Prefs.getPreferences(this).getString(Prefs.FILES_PER_REQUEST, "20")))+1;     
            loadFiles(false, page);
        }
        else
        {
            startActivity(new Intent(this, FileInfosActivity.class));
        }               
    }

    /**
     * @param reload : clean the list before loading new items
     * @param page : if 0, items are prepended, else, items are appended
     */
    private void loadFiles(final boolean reload, final int page)
    {               
        if (!currentlyLoading)
        {           
            currentlyLoading = true;
            showProgressIcon(true);

            new Thread(new Runnable() {
                @Override
                public void run()
                {
                    // REQUESTING WEB SERVICE HERE

                    MainActivity.this.handler.sendEmptyMessage(0);                      
                }
            }).start();
        }
    }

    private void setHandler()
    {
        this.handler = new Handler() {

            @Override
            public void handleMessage(Message msg)
            {
                currentlyLoading = false;                               

                if (filesAdapter == null)
                {
                    filesAdapter = new FilesAdapter(MainActivity.this, MainActivity.this.items);
                    listView.setAdapter(filesAdapter);
                }
                else
                {   
                    filesAdapter.refill(MainActivity.this.items);
                }

                // PROBLEM : LINE TRIGGERING ERROR
                showProgressIcon(false);
            }
        };
    }
}

当我运行应用程序时,一切都很好并且正常工作。 旋转手机时出现问题。

堆栈跟踪:

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.quanturium.androcloud/com.quanturium.androcloud.MainActivity}:
java.lang.ClassCastException: android.view.AbsSavedState$1 cannot be cast to
android.widget.ProgressBar$SavedState
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3347)
at android.app.ActivityThread.access$700(ActivityThread.java:122)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1150)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4340)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassCastException: android.view.AbsSavedState$1 cannot be cast to
android.widget.ProgressBar$SavedState
at android.widget.ProgressBar.onRestoreInstanceState(ProgressBar.java:1093)
at android.view.View.dispatchRestoreInstanceState(View.java:9876)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2330)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2330)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2330)
at android.view.View.restoreHierarchyState(View.java:9854)
at com.android.internal.policy.impl.PhoneWindow.restoreHierarchyState(PhoneWindow.java:1625)
at android.app.Activity.onRestoreInstanceState(Activity.java:906)
at android.app.Activity.performRestoreInstanceState(Activity.java:878)
at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1100)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1933)
... 12 more

actionLayout (@+id/menuItemProgress) 似乎导致了问题。刷新项 (@+id/menuItemRefresh) 一切正常。我在问题所在的地方写了评论。当我注释掉“问题:”行时,轮换效果很好。

I'm using the built in action bar. I want to display in the action bar an animated loader while fetching data, and display a "refresh" icon when not. The simple way I found is to use 2 menu items and show one / hide the other and then the opposite. So here is my menu :

<item android:id="@+id/menuItemRefresh" 
      android:title="Refresh" 
      android:showAsAction="always" 
      android:icon="@drawable/ic_menu_refresh"
      android:visible="false" />
<item android:id="@+id/menuItemProgress"
      android:actionLayout="@layout/progress"
      android:showAsAction="always"
      android:visible="true" />

I'm using an actionLayout (as you can see) because I did not figure out how to create an indeterminate progressbar rotation in my Menu. Here is the corresponding actionLayout :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/layoutProgress" >

    <ProgressBar
        android:id="@+id/menuItemRefresh"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_marginLeft="13dp"
        android:layout_marginRight="13dp" />

</LinearLayout>

My activity :

public class MainActivity extends Activity implements OnItemClickListener
{
    Handler             handler;
    MenuItem            menuItemProgress;
    MenuItem            menuItemRefresh;

    int                 currentPage;
    List<CloudAppItem>  items;
    boolean             currentlyLoading    = false;
    ListView            listView;
    FilesAdapter        filesAdapter;
    LinearLayout        emptyView;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        Log.i("MainActivity", "Started");

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);
        setHandler();
        setListing();

        loadItems();
    }

    @Override
    protected void onResume()
    {
        super.onResume();

        if (isFirstRun() && haveCredentialsChanged())
        {
            setFirstRun(false);
        }

        if (isFirstRun())
        {
            firstRun();
        }
        else if (haveCredentialsChanged())
        {
            setCredentialsChanged(false);
            loadFiles(true, 0);
        }
        else
        {
            loadFiles(false, 0);
        }
    }

    @Override
    public Object onRetainNonConfigurationInstance()
    {
        return items;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {               
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);

        this.menuItemProgress = menu.findItem(R.id.menuItemProgress);
        this.menuItemRefresh = menu.findItem(R.id.menuItemRefresh);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch (item.getItemId())
        {
            case R.id.menuItemPreferences:

                startActivity(new Intent(MainActivity.this, PreferencesActivity.class));

            break;

            case R.id.menuItemRefresh:

                loadFiles(true, 0);

            break;
        }

        return false;
    }

    private boolean isFirstRun()
    {
        return Prefs.getPreferences(this).getBoolean(Prefs.FIRST_RUN, true);
    }

    private void firstRun()
    {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setMessage("First run");
        builder.setCancelable(false);
        builder.setPositiveButton("ok", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which)
            {
                startActivity(new Intent(MainActivity.this, PreferencesActivity.class));
            }
        });

        AlertDialog alert = builder.create();
        alert.show();
    }

    private void setFirstRun(boolean b)
    {
        Prefs.getPreferences(this).edit().putBoolean(Prefs.FIRST_RUN, b).commit();
    }

    private boolean haveCredentialsChanged()
    {
        return Prefs.getPreferences(this).getBoolean(Prefs.CREDENTIALS_CHANGED, false);
    }

    private void setCredentialsChanged(boolean b)
    {
        Prefs.getPreferences(this).edit().putBoolean(Prefs.CREDENTIALS_CHANGED, b).commit();
    }

    private void setListing()
    {
        listView = (ListView) findViewById(R.id.listView);
        emptyView = (LinearLayout) findViewById(R.id.emptyView);
        listView.setOnItemClickListener(this);
        listView.setEmptyView(emptyView);
    }

    @SuppressWarnings("unchecked")
    private void loadItems()
    {
        Object obj = getLastNonConfigurationInstance();
        if (obj != null)
        {
            items = (List<CloudAppItem>) obj;
            MainActivity.this.handler.sendEmptyMessage(0);
        }
        else
        {
            if (!isFirstRun())
                loadFiles(true, 0);
        }
    }

    private void showProgressIcon(boolean show)
    {
        if(this.menuItemProgress != null && this.menuItemRefresh != null)
        {                       
            if(show)
            {               
                this.menuItemProgress.setVisible(true); 
                this.menuItemRefresh.setVisible(false);                         
            }
            else
            {
                this.menuItemRefresh.setVisible(true);

                // PROBLEM : LINE TRIGGERING ERROR
                this.menuItemProgress.setVisible(false);    
            }
        }
    }

    @Override
    public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
    {
        if(this.items.get(arg2).isFake())
        {
            filesAdapter.lastItemLoading = true;
            filesAdapter.notifyDataSetChanged();

            int page = (int)(this.items.size()/Integer.valueOf(Prefs.getPreferences(this).getString(Prefs.FILES_PER_REQUEST, "20")))+1;     
            loadFiles(false, page);
        }
        else
        {
            startActivity(new Intent(this, FileInfosActivity.class));
        }               
    }

    /**
     * @param reload : clean the list before loading new items
     * @param page : if 0, items are prepended, else, items are appended
     */
    private void loadFiles(final boolean reload, final int page)
    {               
        if (!currentlyLoading)
        {           
            currentlyLoading = true;
            showProgressIcon(true);

            new Thread(new Runnable() {
                @Override
                public void run()
                {
                    // REQUESTING WEB SERVICE HERE

                    MainActivity.this.handler.sendEmptyMessage(0);                      
                }
            }).start();
        }
    }

    private void setHandler()
    {
        this.handler = new Handler() {

            @Override
            public void handleMessage(Message msg)
            {
                currentlyLoading = false;                               

                if (filesAdapter == null)
                {
                    filesAdapter = new FilesAdapter(MainActivity.this, MainActivity.this.items);
                    listView.setAdapter(filesAdapter);
                }
                else
                {   
                    filesAdapter.refill(MainActivity.this.items);
                }

                // PROBLEM : LINE TRIGGERING ERROR
                showProgressIcon(false);
            }
        };
    }
}

Everything is fine and working when I run the application.
The problem appears when I rotate the phone.

The stacktrace :

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.quanturium.androcloud/com.quanturium.androcloud.MainActivity}:
java.lang.ClassCastException: android.view.AbsSavedState$1 cannot be cast to
android.widget.ProgressBar$SavedState
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980)
at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3347)
at android.app.ActivityThread.access$700(ActivityThread.java:122)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1150)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4340)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassCastException: android.view.AbsSavedState$1 cannot be cast to
android.widget.ProgressBar$SavedState
at android.widget.ProgressBar.onRestoreInstanceState(ProgressBar.java:1093)
at android.view.View.dispatchRestoreInstanceState(View.java:9876)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2330)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2330)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:2330)
at android.view.View.restoreHierarchyState(View.java:9854)
at com.android.internal.policy.impl.PhoneWindow.restoreHierarchyState(PhoneWindow.java:1625)
at android.app.Activity.onRestoreInstanceState(Activity.java:906)
at android.app.Activity.performRestoreInstanceState(Activity.java:878)
at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1100)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1933)
... 12 more

The actionLayout (@+id/menuItemProgress) seems to cause the problem. Everything is fine with the refresh item (@+id/menuItemRefresh). I wrote as a comment where the problem is. When I comment OUT the line "PROBLEM : " the rotation works well.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

作妖 2025-01-04 18:02:20

Android 似乎无法恢复视图的保存状态。

发生这种情况是因为您有两个具有相同 ID 的视图。由于它们不属于同一类,因此转换失败并崩溃。

<item android:id="@+id/menuItemRefresh" 

<ProgressBar
       android:id="@+id/menuItemRefresh"

是你的问题。

Android doesn't seem to be able to restore your view's saved state.

This happens because you have two views with the same ID. Since they are not of the same class, the cast fails and crashes.

<item android:id="@+id/menuItemRefresh" 

and

<ProgressBar
       android:id="@+id/menuItemRefresh"

This is your problem.

装纯掩盖桑 2025-01-04 18:02:20

我遇到了同样的问题,并通过将以下内容添加到 AndroidManifest.xml 中的活动声明中来解决它:

android:configChanges="orientation|screenSize|keyboardHidden"

此页面引导我找到了解决方案。希望这有帮助。

I had the same problem and solved it by adding the following to my activity declaration in AndroidManifest.xml:

android:configChanges="orientation|screenSize|keyboardHidden"

This page led me to the solution. Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文