ActionBar的Overlay模式如何不遮盖顶部内容的问题

转载自:点击打开链接

关于actionbar的overlay模式请参考 如何让android的actionbar浮动且透明 一文。这篇文章讲的是如何在这种模式下让actionbar不遮住顶部的内容。

这一般是这样的场景,在一个ListView显示图片的界面中,当ListView向下滑动的时候,actionbar是是浮动在GridView上面一层的,但是当ListView滚动到顶部,顶部的内容是完全显示出来的,当然这种情况一般ActionBar我们会做成透明效果。

其实很多人都能想到的是,将ListView加上一个高度和actionbar的高度相同的header不就行了吗?

但是,难点是如何得到actionbar的高度。

actionbar的高度其实是在android系统主题的资源文件中定义的,如果你没有主动去修改actionbar的高度,那么可以通过下面的代码来获取:

1
2
3
4
TypedArray actionbarSizeTypedArray = getActivity().obtainStyledAttributes( new int[] {
         android.R.attr.actionBarSize
});
float h = actionbarSizeTypedArray.getDimension(0, 0);

但是这种方式并不太规范,而且在android4.4之后,statusbar所在的区域也是可以显示内容的,这时你还得去计算statusbar的高度。

其实FrameLayout 中boolean fitSystemWindows(Rect insets)方法的insets参数就包含了非内容区域的高度。fitSystemWindows会在加载的时候被调用,如果我们在ListView重写fitSystemWindows不就可以知道该给ListView添加多高的HeaderView了吗?

但是一般我们不希望这样用ListView,因为使用重写的ListView的几率实在太大了(下拉刷新ListView等),而采取另外的方法,把ListView和一个重写了fitSystemWindows方法的FrameLayout放在同一个FrameLayout中,然后通过回调的方式来通知ListView已经获取到了actionbar(或者+statusbar)的高度了。

我们将这个实现了fitSystemWindows方法的FrameLayout命名为:DrawInsetsFrameLayout

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
  * Copyright 2014 Google Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
package com.example.drawinsetsframelayoutdemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
/**
  * A layout that draws something in the insets passed to {@link #fitSystemWindows(Rect)}, i.e. the area above UI chrome
  * (status and navigation bars, overlay action bars).
  */
public class DrawInsetsFrameLayout extends FrameLayout {
     private Drawable mInsetBackground;
     private Rect mInsets;
     private Rect mTempRect = new Rect();
     private OnInsetsCallback mOnInsetsCallback;
     public DrawInsetsFrameLayout(Context context) {
         super (context);
         init(context, null , 0);
     }
     public DrawInsetsFrameLayout(Context context, AttributeSet attrs) {
         super (context, attrs);
         init(context, attrs, 0);
     }
     public DrawInsetsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
         super (context, attrs, defStyle);
         init(context, attrs, defStyle);
     }
     private void init(Context context, AttributeSet attrs, int defStyle) {
         final TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.DrawInsetsFrameLayout, defStyle, 0);
         if (a == null ) {
             return ;
         }
         mInsetBackground = a.getDrawable(R.styleable.DrawInsetsFrameLayout_insetBackground);
         a.recycle();
         setWillNotDraw( true );
     }
     @Override
     protected boolean fitSystemWindows(Rect insets) {
         mInsets = new Rect(insets);
         setWillNotDraw(mInsetBackground == null );
         postInvalidateOnAnimation();
         if (mOnInsetsCallback != null ) {
             mOnInsetsCallback.onInsetsChanged(insets);
         }
         return true ; // consume insets
     }
     @Override
     protected void onDraw(Canvas canvas) {
         super .onDraw(canvas);
         int width = getWidth();
         int height = getHeight();
         if (mInsets != null && mInsetBackground != null ) {
             // Top
             mTempRect.set(0, 0, width, mInsets.top);
             mInsetBackground.setBounds(mTempRect);
             mInsetBackground.draw(canvas);
             // Bottom
             mTempRect.set(0, height - mInsets.bottom, width, height);
             mInsetBackground.setBounds(mTempRect);
             mInsetBackground.draw(canvas);
             // Left
             mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
             mInsetBackground.setBounds(mTempRect);
             mInsetBackground.draw(canvas);
             // Right
             mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
             mInsetBackground.setBounds(mTempRect);
             mInsetBackground.draw(canvas);
         }
     }
     @Override
     protected void onAttachedToWindow() {
         super .onAttachedToWindow();
         if (mInsetBackground != null ) {
             mInsetBackground.setCallback( this );
         }
     }
     @Override
     protected void onDetachedFromWindow() {
         super .onDetachedFromWindow();
         if (mInsetBackground != null ) {
             mInsetBackground.setCallback( null );
         }
     }
     /**
      * Allows the calling container to specify a callback for custom processing when insets change (i.e. when
      * {@link #fitSystemWindows(Rect)} is called. This is useful for setting padding on UI elements based on
      * UI chrome insets (e.g. a Google Map or a ListView). When using with ListView or GridView, remember to set
      * clipToPadding to false.
      */
     public void setOnInsetsCallback(OnInsetsCallback onInsetsCallback) {
         mOnInsetsCallback = onInsetsCallback;
     }
     public static interface OnInsetsCallback {
         public void onInsetsChanged(Rect insets);
     }
}

其中最主要的就是fitSystemWindows方法,其他的不过是绘制DrawInsetsFrameLayout在actionbar部分的显示颜色而已。

如何使用DrawInsetsFrameLayout呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.example.drawinsetsframelayoutdemo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class MainActivity extends Activity {
     private ListView listView;
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
         listView= (ListView) findViewById(R.id.listview);
         String[] from = { "Text" , "Button" };
         int[] to = { R.id.text, R.id.button };
         List<Map<String, ?>> list = new ArrayList<Map<String, ?>>();
         for (int i = 0; i < 103; i++) {
             Map<String, String> m = new HashMap<String, String>();
             m.put( "Text" , "Text" + i);
             m.put( "Button" , "Button" + i);
             list.add(m);
         }
         SimpleAdapter adapter = new SimpleAdapter( this , list, R.layout.listitem, from, to);
         listView.setAdapter(adapter);
                                                                                                                                                                                                                          
         DrawInsetsFrameLayout drawInsetsFrameLayout = (DrawInsetsFrameLayout) findViewById(R.id.my_draw_insets_layout);
         drawInsetsFrameLayout.setOnInsetsCallback( new DrawInsetsFrameLayout.OnInsetsCallback() {
             @Override
             public void onInsetsChanged(Rect insets) {
                 // Update the map padding (inset the compass, zoom buttons, attribution, etc.)
                 Log.i( "" , "insets.top = " + insets.top);
                 View headerView = new View(MainActivity. this );
                 AbsListView.LayoutParams params = new AbsListView.LayoutParams(LayoutParams.FILL_PARENT, insets.top);
                                                                                                                                                                                                                                 
                 headerView.setLayoutParams(params);
                 headerView.setBackgroundColor(0x33000000);
                 listView.addHeaderView(headerView);
             }
         });
     }
                                                                                                                                                                                                                 
}

设置actionbar风格的xml文件:

1
2
3
4
5
6
7
8
9
<!-- Application theme. -->
<style name= "AppTheme" parent= "AppBaseTheme" >
     <item name= "android:actionBarStyle" >@style/TranslucentActionBar</item>
     <item name= "android:windowActionBarOverlay" > true </item>
     <item name= "android:windowTranslucentStatus" > true </item>
</style>
<style name= "TranslucentActionBar" parent= "android:Widget.Holo.Light.ActionBar.Solid.Inverse" >
     <item name= "android:background" >@ null </item>
</style>

其中TranslucentActionBar是为DrawInsetsFrameLayout自定义的一个属性,而activity的actionbar和statusbar在这里我们都是设置成了浮动模式的,注意<item name="android:windowTranslucentStatus">true</item>

最后是布局代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version= "1.0" encoding= "utf-8" ?>
<FrameLayout xmlns:android= "http://schemas.android.com/apk/res/android"
     xmlns:yourapp= "http://schemas.android.com/apk/res-auto"
     android:layout_width= "fill_parent"
     android:layout_height= "fill_parent"
     >
     <ListView
         android:id= "@+id/listview"
         android:layout_width= "match_parent"
         android:layout_height= "match_parent"
     />
     <com.example.drawinsetsframelayoutdemo.DrawInsetsFrameLayout
         android:id= "@+id/my_draw_insets_layout"
         android:layout_width= "match_parent"
         android:layout_height= "match_parent"
         yourapp:insetBackground= "#9000" />
</FrameLayout>

效果图:

-----


完整的demo代码在这里下载:http://download.csdn.net/detail/jianghejie123/7992853


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在 Android 应用程序中实现顶部返回按钮,可以使用 ActionBar。ActionBar 是 Android 提供的一种 UI 控件,通常位于应用程序的顶部,用于显示应用程序名称、菜单和其他选项。 要实现 ActionBar 中的顶部返回按钮,请按照以下步骤操作: 1. 打开 Android Studio,创建一个新的空白项目。 2. 在 res/values/styles.xml 文件中添加以下代码: ``` <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> </style> ``` 这将为应用程序创建一个基本的主题,其中包含一个暗色 ActionBar。 3. 在 AndroidManifest.xml 文件中,将应用程序的主题设置为上一步中创建的主题: ``` <application android:theme="@style/AppTheme" ... ``` 4. 在 MainActivity.java 文件中,添加以下代码: ``` @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 启用 ActionBar 返回按钮 getSupportActionBar().setDisplayHomeAsUpEnabled(true); } // 处理 ActionBar 返回按钮点击事件 @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: // 在此处添加返回按钮点击后的处理逻辑 finish(); return true; } return super.onOptionsItemSelected(item); } ``` 这将启用 ActionBar 中的返回按钮,并在用户点击该按钮时关闭当前活动。 5. 运行应用程序,并确保 ActionBar 中显示了返回按钮。单击该按钮,应该会关闭当前活动。 注意:如果您的应用程序需要导航到其他活动,则可以在 onOptionsItemSelected() 方法中添加相应的 Intent。例如: ``` case android.R.id.home: Intent intent = new Intent(this, OtherActivity.class); startActivity(intent); finish(); return true; ``` 这将使 ActionBar 返回按钮导航到 OtherActivity。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值