在操作栏上方显示视图

41
有没有一种方法可以在操作栏顶部渲染视图?我想创建一个小提示框,指向操作栏中的项目。我知道用设置视图的Toast会在操作栏上方呈现。有人知道如何使用视图做到这一点吗?
我尝试使用布局属性layout_gravity="top"FrameLayout,然后将其充气并添加到正在运行的活动布局中。
提前感谢您。 编辑: 这是我所想的图片: enter image description here 编辑: 也许需要更多细节。我正在寻找一种方法,或者找出是否可能将视图添加到活动的视图层次结构中,以便它最后呈现。
类似于CSS,我希望这个特定视图(即图像中的蓝色浮动框)具有更高的z-index顺序,以使其在活动的操作栏区域的顶部呈现。该视图与操作栏无任何关联,只是在其上方绘制。

你找到解决这个问题的方法了吗?我现在想在我的应用程序中执行相同的任务,但是我下面没有看到任何相关的答案。 - MM.
@MM 看一下我的回答,我刚刚添加了它。 - Sean
10个回答

35

我本来想实现其他功能,但需要类似于这个的解决方案。我需要绘制一个不透明的层覆盖整个屏幕,甚至是操作栏 - 就像对话框一样。我是这样做的:

ViewGroup vg = (ViewGroup)(getWindow().getDecorView().getRootView());
vg.addView(myNewView, params);

这可以用于在屏幕上的任何位置绘制任何内容。

更新:您真的不应再使用ActionBar,如果您像Android推荐的那样使用Toolbar,您将不会遇到此问题。 Toolbar 将像常规视图一样放置在您的活动 XML 中,您可以对其进行任何操作。它还完全向后兼容。 https://developer.android.com/training/appbar/setting-up


1
@ekashking params 是一个 LayoutParams 实例。它用于确定视图的布局方式。例如:new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) - Mavamaarten

21

在自己苦苦挣扎一段时间后,这是解决方案(已测试-运行良好):

一般步骤如下:

  1. 创建一个包装视图
  2. 分离屏幕视图子项,放置包装器并附加子项
  3. 将内容填充到子项中
  4. 使用包装器将帮助您完全控制操作栏和其下的所有内容。
  5. 现在,使用包装器,您可以将“兄弟”添加到操作栏/主区域。该兄弟正是您在图像中描述的。

让我们看看一些代码。

首先,创建一个方法来帮助创建一个包装器视图。 该包装器将放置在整个屏幕和应用程序内容之间。作为 ViewGroup,您可以稍后完全控制其内容。

private ViewGroup setContentViewWithWrapper(int resContent) {
        ViewGroup decorView = (ViewGroup) this.getWindow().getDecorView();
        ViewGroup decorChild = (ViewGroup) decorView.getChildAt(0);

        // Removing decorChild, we'll add it back soon
        decorView.removeAllViews();

        ViewGroup wrapperView = new FrameLayout(this);

        // You should set some ID, if you'll want to reference this wrapper in that manner later
        //
        // The ID, such as "R.id.ACTIVITY_LAYOUT_WRAPPER" can be set at a resource file, such as:
        //  <resources xmlns:android="http://schemas.android.com/apk/res/android">
        //      <item type="id" name="ACTIVITY_LAYOUT_WRAPPER"/>
        //  </resources>
        //
        wrapperView.setId(R.id.ACTIVITY_LAYOUT_WRAPPER);

        // Now we are rebuilding the DecorView, but this time we 
        // have our wrapper view to stand between the real content and the decor
        decorView.addView(wrapperView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        wrapperView.addView(decorChild, decorChild.getLayoutParams());
        LayoutInflater.from(this).inflate(getActivityLayout(), 
                    (ViewGroup)((LinearLayout)wrapperView.getChildAt(0)).getChildAt(1), true);

        return wrapperView;
    }

现在,干扰常规Activity的创建,而不是使用setContentView方法,而是使用我们创建的方法。
    @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // DON'T CALL `setContentView`, 
    // we are replacing that line with this code:
    ViewGroup wrapperView = setContentViewWithWrapper(R.layout.activity_layout);

    // Now, because the wrapper view contains the entire screen (including the notification bar
    // which is above the ActionBar) I think you'll find it useful to know the exact Y where the 
    // action bar is located.
    // You can use something like that:
    ViewGroup actionBar = (ViewGroup)((LinearLayout)wrapperView.getChildAt(0)).getChildAt(0);
    int topOffset = actionBar.getTop();

    // Now, if you'll want to add a view:
    //  1. Create new view
    //  2. Set padding top - use "topOffset"
    //  3. Add the view to "wrapperView"
    //  4. The view should be set at front. if not - try calling to "bringToFront()"
}

这就是全部内容。

注释

  • 我使用 Android的层级查看器 来理解正确的层次结构。(没有猜到那些 01 的索引)
  • 如果您在活动中使用某种菜单抽屉,您可能需要进行一些不同的配置,因为抽屉已经为您创建了该包装器
  • 我通过查看 这个伟大的库 学到了很多东西

编辑: 有关较新的Android版本的进一步支持,请参阅@CristopherOyarzúnAltamirano的答案

祝你好运!


嘿,我在实现Android 4.4的解决方案时遇到一些问题。我得到了这个异常,你有什么想法吗?com.android.internal.widget.ActionBarOverlayLayout无法转换为android.widget.LinearLayout。 - Dan1one
2
这适用于ICS和Jelly,但Ginger和Kitkat以不同的方式绘制层次结构。 - Cristopher Oyarzún Altamirano
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - DroidLearner
@DroidLearner,请参考CristopherOyarzúnAltamirano的答案,以获取有关较新Android版本的进一步支持。 - Sean
1
那个答案应该被更新。会导致“强制关闭”。 - 1111161171159459134
显示剩余3条评论

14

有一种更简单的方法可以实现这个。ActionBar将其布局保存在ActionBarContainer类中,该类直接继承自FrameLayout。因此,为了在ActionBar上显示某些内容,您需要获取对ActionBarContainer的引用并将自定义视图添加到其中。下面是代码

    int abContainerViewID = getResources().getIdentifier("action_bar_container", "id", "android");     
    FrameLayout actionBarContainer = (FrameLayout)findViewById(abContainerViewID);      
    LayoutInflater myinflater = getLayoutInflater();        
    View customView = myinflater.inflate(R.layout.yourCustomeViewLayout, null);
    actionBarContainer.addView(customView);

1
对我不起作用。即使action_bar_container在视图层次结构中,actionBarContainer仍为空。 - illusionJJ

5

我发现了这个解决方法,基于@Sean的答案:

        //This is for Jelly, ICS, Honeycomb
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){
            LayoutInflater.from(this).inflate(resContent, (ViewGroup)((LinearLayout)wrapperView.getChildAt(0)).getChildAt(1), true);}
        //This is for KitKat and Jelly 4.3
        else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){
            LayoutInflater.from(this).inflate(resContent, (ViewGroup) (((ViewGroup) wrapperView.getChildAt(0)).getChildAt(0)), true);}
        //This is for Ginger
        else{
            LayoutInflater.from(this).inflate(resContent, (ViewGroup) ((LinearLayout)((FrameLayout) wrapperView.getChildAt(0)).getChildAt(0)).getChildAt(1), true);}

1
我在Gingerbird设备上使用这个方法时遇到了错误。我将其编辑为以下内容:LayoutInflater.from(this).inflate(resContent, (ViewGroup) ((FrameLayout) decorChild), true); - Shayan_Aryan

5
我找到了一个更简单的方法来实现这个功能。 我只需将 android:translationZ="10dp" 应用于需要覆盖操作栏的视图。
我选择了 10dp,但实际上它可以是任何你想要的值,只要它比操作栏的高度高即可。
<ImageView
      android:layout_width="100dp"
      android:layout_height="100dp"
      android:translationZ="10dp"
      android:src="@drawable/potatoe" />

此外,不要担心Android studio给出的以下警告:

"translationZ不能在API<21中使用"。

它会被忽略的,但你其实并不关心,因为工具栏不应该覆盖你的视图,在API低于21的情况下。


不起作用。imageView 仍然会在应用栏后面。 - coolcool1994

1

1
尝试使用ActionBar.setCustomView()。这是改变屏幕上该区域外观的唯一方法。您无法将视图插入“ActionBar”上方的区域,因为该区域基本上由系统控制。另一方面,您可以为其提供自己的布局。
如果您详细解释您要做什么,回答者可能会有更好的设计想法。

很棒,我已经包含了一个我所想的图片。我看到Toast可以有一个视图集。我很好奇它们如何在操作栏的顶部呈现它们的视图。 - lunaleaps

0

这里的解释都太长了:

将您的主布局文件包装到顶级ViewGroup中(例如,将CoordinatorLayout包装到FrameLayout中),然后在新的顶级布局中注入或声明视图。该视图将出现在操作栏上方。


0
在你的menu.xml文件中使用android:actionLayout
<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_id"
      android:title="@string/menu_string_to_show"
      android:icon="@drawable/ic_menu_icon_name"
      android:showAsAction="always"
      android:actionLayout="@layout/action_button_foo" /></menu>

然后创建你的 action_button_foo.xml 布局:

<?xml version="1.0" encoding="utf-8"?><TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/menu_string_to_show"
android:drawableLeft="@drawable/ic_menu_icon_name"
android:background="@drawable/bg_btn_action_bar"
android:clickable="true" />

处理点击事件,按照以下步骤进行:

@Overridepublic boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.my_menu, menu);

final MenuItem item = menu.findItem(R.id.menu_id);
item.getActionView().setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        onOptionsItemSelected(item);
    }
});

return super.onCreateOptionsMenu(menu);}

就是这样 :)


谢谢您的快速回复!但我认为这是为操作栏创建菜单项?我只想在操作栏顶部绘制一个视图。但是这个视图不应该是操作栏的一部分,它是非响应性的。 - lunaleaps
什么是视图? - Shajeel Afzal
这将是布局文件中的TextView和图像背景。如果您查看我在原始帖子中添加的图像,它将是浮动的蓝色框。 - lunaleaps

0

(参考: http://www.vogella.com/articles/AndroidActionBar/article.html
ActionBar中的自定义视图
您还可以向 ActionBar 添加自定义视图。为此,您可以使用 ActionView 类的 setCustomView 方法。您还必须通过传递 ActionBar.DISPLAY_SHOW_CUSTOM 标志来使用 setDisplayOptions() 方法启用自定义视图的显示选项。

例如,您可以定义一个包含 EditText 元素的布局文件。

<?xml version="1.0" encoding="utf-8"?>
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/searchfield"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:inputType="textFilter" >

可以通过以下代码将此布局分配给ActionBar。示例代码允许将侦听器附加到自定义视图。

package com.vogella.android.actionbar.customviews;

import android.app.ActionBar;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

ActionBar actionBar = getActionBar();
// add the custom view to the action bar
actionBar.setCustomView(R.layout.actionbar_view);
EditText search = (EditText) actionBar.getCustomView().findViewById(R.id.searchfield);
search.setOnEditorActionListener(new OnEditorActionListener() {

  @Override
  public boolean onEditorAction(TextView v, int actionId,
      KeyEvent event) {
    Toast.makeText(MainActivity.this, "Search triggered",
        Toast.LENGTH_LONG).show();
    return false;
  }
});
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM
    | ActionBar.DISPLAY_SHOW_HOME);
}
} 

啊,好的,从我的理解来看,您正在添加一个Toast通知,当我在操作栏上点击EditText时。我更想知道如何在视觉上在操作栏顶部呈现视图/布局。就像我知道Android是如何创建视图层次结构的。我该如何插入一个视图,以便它作为层次结构的最后一个被渲染,从而出现在操作栏“上方”,即z-index排序或“将视图置于前景”?还是这不可能?无论如何,感谢您的帮助Vishal。 - lunaleaps
所以你的意思是说你必须在不使用setCustomView的情况下在操作栏上插入自定义视图。我认为你需要去自定义操作栏。 - Vishal Vijay
最好你自定义操作栏(创建你自己的操作栏)。你可以随心所欲地做任何事情。 - Vishal Vijay
从Github下载ActionBarSherlock并查看他们如何实现ActionBar。 - Vishal Vijay
当然,这会对你有所帮助 https://dev59.com/M2nWa4cB1Zd3GeqP3bUS - Vishal Vijay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接