详解Android中的沉浸式状态栏效果实例_Android

无意间了解到沉浸式状态栏,感觉贼拉的高大上,于是就是试着去了解一下,就有了这篇文章。下面就来了解一下啥叫沉浸式状态栏。传统的手机状态栏是呈现出黑色条状的,有的和手机主界面有很明显的区别。这一样就在一定程度上牺牲了视觉宽度,界面面积变小。

Google从android kitkat(Android 4.4)开始,给我们开发者提供了一套能透明的系统ui样式给状态栏和导航栏,这样的话就不用向以前那样每天面对着黑乎乎的上下两条黑栏了,还可以调成跟Activity一样的样式,形成一个完整的主题,和IOS7.0以上系统一样了,沉浸式状态栏和主界面颜色和谐一体,视觉效果更加炫酷。

不过虽然听上去好像是很高大上的沉浸式效果,实际看上去貌似就是将内容全屏化了而已嘛。其实这算是一个争议点了。不少人纠结于沉浸式状态栏到底是将屏幕显示内容扩大还是仅仅是改变状态栏、标题栏的颜色。其实我更倾向于后者。在4.4之前状态栏一直是黑色的,在4.4中带来了 windowTranslucentStatus 这一特性,因此可以实现给状态栏设置颜色,视觉上的效果,感觉容器部分和状态栏、标题栏融为一体,更加直接的说就是改变状态栏、标题栏的颜色,当时可以根据界面颜色改变状态栏、标题栏的颜色实现跟加完整的界面显示,这应该是沉浸式状态栏受追捧的原因吧。

谷歌并没有给出沉浸式状态栏这个概念,谷歌只说了沉浸式模式(Immersive Mode)。不过沉浸式状态栏这个名字其实挺不错,只能随大众,但是Android的环境并没有IOS环境一样特别统一,比如华为rom的跟小米rom的虚拟按键完全不一样,并且安卓版本众多涉及到版本兼容问题,所有Android开发者不容易。这点在沉浸式状态栏的开发中显得尤为重要。如果你在4.4之前的机子上显示沉浸式状态栏的话,经常出现一些意想不到的结果。

沉浸式是APP界面图片延伸到状态栏, 应用本身沉浸于状态栏,所以如果第三方的软件没有为状态栏分配图片,那么自然就是黑色。顶端的状态栏和下面的虚拟按键都隐藏,需要的时候从边缘划出。沉浸模式。当启用该模式,应用程序的界面将占据整个屏幕,系统自动将隐藏系统的状态栏和导航栏,让应用程序内容可以在最大显示范围呈现,增加大屏体验,而当需要查看通知的时候只需要从顶部向下滑动就能呼出通知栏。

沉浸模式实际上有两种: 一种叫“沉浸模式”,状态栏和虚拟按钮会自动隐藏、应用自动全屏,这种模式下,应用占据屏幕的全部空间, 只有当用户从屏幕的上方边沿处向下划动时, 才会退出沉浸模式, 用户触摸屏幕其它部分时, 不会退出该模式, 这种模式比较适用于阅读器、 杂志类应用。另外一种叫“黏性沉浸模式”,让状态栏和虚拟按钮半透明,应用使用屏幕的全部空间, 当用户从屏幕的上方边沿处向下滑动时,也不会退出该模式, 但是系统界面 (状态栏、 导航栏) 将会以半透明的效果浮现在应用视图之上 , 只有当用户点击系统界面上的控件时, 才会退出黏性沉浸模式。

下面来说一说具体的实现。一个Android应用程序的界面上其实是有很多系统元素的,有状态栏、ActionBar、导航栏等。而打造沉浸式模式的用户体验,就是要将这些系统元素进行整合,当主界面改变时,状态栏、ActionBar、导航栏同时也发生改变。这里先调用getWindow().getDecorView()方法获取到了当前界面的DecorView,然后调用它的setSystemUiVisibility()方法来设置系统UI元素的可见性。其中,SYSTEM_UI_FLAG_FULLSCREEN表示全屏的意思,也就是会将状态栏隐藏。另外,根据Android的设计建议,ActionBar是不应该独立于状态栏而单独显示的,因此状态栏如果隐藏了,我们同时也需要调用ActionBar的hide()方法将ActionBar也进行隐藏这种效果不叫沉浸式状态栏,也完全没有沉浸式状态栏这种说法,我们估且可以把它叫做透明状态栏效果吧。

隐藏状态栏:

setContentView(R.layout.activity_main); //再该方法后执行
if (Build.VERSION.SDK_INT >= 21) {
  View decorView = getWindow().getDecorView();
  int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
      | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
  decorView.setSystemUiVisibility(option);
  getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ActionBar actionBar = getSupportActionBar();
actionBar.hide();

具体的沉浸效果该如何实现呢,系统提供实现沉浸式状态栏的方法,通过WindowManager来实现,可分为两步:

1. 在需要实现沉浸式状态栏的Activity的布局中添加以下参数

android:fitsSystemWindows="true"
android:clipToPadding="true"

 2. 在Activity的setContentView()方法后面调用初始化的方法即可。

private void initState() {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
      //透明状态栏
       getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
      //透明导航栏
       getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
      }
    }

当上述的实现效果,其实并不好, 没有在布局中设置clipToPadding为true的时候,会对应用的顶部Toolbar进行拉伸,在布局中两个参数都进行设置后,顶部状态栏变成了白色。这样,我在github上找到一个很好的沉浸状态栏效果,来看一下。

首先添加依赖,导入下面的包。有时候可能会出现版本不统一的问题,依次保证联网的情况下点击一下同步android studio会自动下载包。

compile 'com.jaeger.statusbaruitl:library:1.2.5'

在自定义控件中实现的进本逻辑,代码较长。

package com.xiaoyuan;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.widget.ScrollView;

import java.util.ArrayList;

/**
 * @author Emil Sj�lander - sjolander.emil@gmail.com
 */
public class StickyScrollView extends ScrollView {

  /**
   * Tag for views that should stick and have constant drawing. e.g.
   * TextViews, ImageViews etc
   */
  public static final String STICKY_TAG = "sticky";

  /**
   * Flag for views that should stick and have non-constant drawing. e.g.
   * Buttons, ProgressBars etc
   */
  public static final String FLAG_NONCONSTANT = "-nonconstant";

  /**
   * Flag for views that have aren't fully opaque
   */
  public static final String FLAG_HASTRANSPARANCY = "-hastransparancy";

  /**
   * Default height of the shadow peeking out below the stuck view.
   */
  private static final int DEFAULT_SHADOW_HEIGHT = 10; // dp;
  /**
   * XKJ add for add 50dp offset of top
   */
  private static int MIN_STICK_TOP = 100;// px
  //  private static final int MIN_STICK_TOP = 0;
  private ArrayList<View> stickyViews;
  private View currentlyStickingView;
  private float stickyViewTopOffset;
  private int stickyViewLeftOffset;
  private boolean redirectTouchesToStickyView;
  private boolean clippingToPadding;
  private boolean clipToPaddingHasBeenSet;

  private int mShadowHeight;
  private Drawable mShadowDrawable;
  private OnScrollChangedListener mOnScrollHandler = null;
  private IOnScrollToEnd mOnScrollToEnd = null;

  private final Runnable invalidateRunnable = new Runnable() {

    @Override
    public void run() {
      if (currentlyStickingView != null) {
        int l = getLeftForViewRelativeOnlyChild(currentlyStickingView);
        int t = getBottomForViewRelativeOnlyChild(currentlyStickingView);
        int r = getRightForViewRelativeOnlyChild(currentlyStickingView);
        int b = (int) (getScrollY() + (currentlyStickingView.getHeight() + stickyViewTopOffset));
        invalidate(l, t, r, b);
      }
      postDelayed(this, 16);
    }
  };

  public StickyScrollView(Context context) {
    this(context, null);
  }

  public StickyScrollView(Context context, AttributeSet attrs) {
    this(context, attrs, android.R.attr.scrollViewStyle);
  }

  public StickyScrollView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    setup();

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StickyScrollView, defStyle, 0);

    final float density = context.getResources().getDisplayMetrics().density;
    int defaultShadowHeightInPix = (int) (DEFAULT_SHADOW_HEIGHT * density + 0.5f);

    mShadowHeight = a.getDimensionPixelSize(R.styleable.StickyScrollView_stuckShadowHeight,
        defaultShadowHeightInPix);

    int shadowDrawableRes = a.getResourceId(R.styleable.StickyScrollView_stuckShadowDrawable, -1);

    if (shadowDrawableRes != -1) {
      mShadowDrawable = context.getResources().getDrawable(shadowDrawableRes);
    }

    a.recycle();

  }

  /**
   * Sets the height of the shadow drawable in pixels.
   *
   * @param height
   */
  public void setShadowHeight(int height) {
    mShadowHeight = height;
  }

  public void setup() {
    stickyViews = new ArrayList<View>();
  }

  private int getLeftForViewRelativeOnlyChild(View v) {
    int left = v.getLeft();
    while (v.getParent() != getChildAt(0)) {
      v = (View) v.getParent();
      left += v.getLeft();
    }
    return left;
  }

  private int getTopForViewRelativeOnlyChild(View v) {
    int top = v.getTop();
    while (v.getParent() != getChildAt(0)) {
      v = (View) v.getParent();
      top += v.getTop();
    }
    return top;
  }

  private int getRightForViewRelativeOnlyChild(View v) {
    int right = v.getRight();
    while (v.getParent() != getChildAt(0)) {
      v = (View) v.getParent();
      right += v.getRight();
    }
    return right;
  }

  private int getBottomForViewRelativeOnlyChild(View v) {
    int bottom = v.getBottom();
    while (v.getParent() != getChildAt(0)) {
      v = (View) v.getParent();
      bottom += v.getBottom();
    }
    return bottom;
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    if (!clipToPaddingHasBeenSet) {
      clippingToPadding = true;
    }
    notifyHierarchyChanged();
  }

  @Override
  public void setClipToPadding(boolean clipToPadding) {
    super.setClipToPadding(clipToPadding);
    clippingToPadding = clipToPadding;
    clipToPaddingHasBeenSet = true;
  }

  @Override
  public void addView(View child) {
    super.addView(child);
    findStickyViews(child);
  }

  @Override
  public void addView(View child, int index) {
    super.addView(child, index);
    findStickyViews(child);
  }

  @Override
  public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
    super.addView(child, index, params);
    findStickyViews(child);
  }

  @Override
  public void addView(View child, int width, int height) {
    super.addView(child, width, height);
    findStickyViews(child);
  }

  @Override
  public void addView(View child, android.view.ViewGroup.LayoutParams params) {
    super.addView(child, params);
    findStickyViews(child);
  }

  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    if (currentlyStickingView != null) {
      canvas.save();
      canvas.translate(getPaddingLeft() + stickyViewLeftOffset, getScrollY() + stickyViewTopOffset
          + (clippingToPadding ? getPaddingTop() : 0));

      canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth() - stickyViewLeftOffset,
          currentlyStickingView.getHeight() + mShadowHeight + 1);

      if (mShadowDrawable != null) {
        int left = 0;
        int right = currentlyStickingView.getWidth();
        int top = currentlyStickingView.getHeight();
        int bottom = currentlyStickingView.getHeight() + mShadowHeight;
        mShadowDrawable.setBounds(left, top, right, bottom);
        mShadowDrawable.draw(canvas);
      }

      canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth(),
          currentlyStickingView.getHeight());
      if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)) {
        showView(currentlyStickingView);
        currentlyStickingView.draw(canvas);
        hideView(currentlyStickingView);
      } else {
        currentlyStickingView.draw(canvas);
      }
      canvas.restore();
    }
  }

  @Override
  public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
      redirectTouchesToStickyView = true;
    }

    if (redirectTouchesToStickyView) {
      redirectTouchesToStickyView = currentlyStickingView != null;
      if (redirectTouchesToStickyView) {
        redirectTouchesToStickyView = ev.getY() <= (currentlyStickingView.getHeight() + stickyViewTopOffset)
            && ev.getX() >= getLeftForViewRelativeOnlyChild(currentlyStickingView)
            && ev.getX() <= getRightForViewRelativeOnlyChild(currentlyStickingView);
      }
    } else if (currentlyStickingView == null) {
      redirectTouchesToStickyView = false;
    }
    if (redirectTouchesToStickyView) {
      ev.offsetLocation(0, -1
          * ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView)));

      // XKJ add TODO: remove this
      currentlyStickingView.invalidate();
    }
    return super.dispatchTouchEvent(ev);
  }

  private boolean hasNotDoneActionDown = true;

  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    if (redirectTouchesToStickyView) {
      ev.offsetLocation(0,
          ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView)));
    }

    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
      hasNotDoneActionDown = false;
    }

    if (hasNotDoneActionDown) {
      MotionEvent down = MotionEvent.obtain(ev);
      down.setAction(MotionEvent.ACTION_DOWN);
      super.onTouchEvent(down);
      hasNotDoneActionDown = false;
    }

    if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) {
      hasNotDoneActionDown = true;
    }

    return super.onTouchEvent(ev);
  }

  @Override
  protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);
    doTheStickyThing();
    if (mOnScrollHandler != null) {
      mOnScrollHandler.onScrollChanged(l, t, oldl, oldt);
    }
    int maxScroll = getChildAt(0).getHeight() - getHeight();
    if (getChildCount() > 0 && t == maxScroll) {
      if (mOnScrollToEnd != null) {
        mOnScrollToEnd.onScrollToEnd();
      }
    }
  }

  public void setOnScrollListener(OnScrollChangedListener handler) {
    mOnScrollHandler = handler;
  }

  public interface OnScrollChangedListener {
    public void onScrollChanged(int l, int t, int oldl, int oldt);
  }

  public interface IOnScrollToEnd {
    public void onScrollToEnd();
  }

  public void setOnScrollToEndListener(IOnScrollToEnd handler) {
    mOnScrollToEnd = handler;
  }

  private void doTheStickyThing() {
    View viewThatShouldStick = null;
    View approachingView = null;
    for (View v : stickyViews) {
      int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop())
          - MIN_STICK_TOP;// add 50dp

      if (viewTop <= 0) {
        if (viewThatShouldStick == null
            || viewTop > (getTopForViewRelativeOnlyChild(viewThatShouldStick) - getScrollY() + (clippingToPadding ? 0
            : getPaddingTop()))) {
          viewThatShouldStick = v;
        }
      } else {
        if (approachingView == null
            || viewTop < (getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0
            : getPaddingTop()))) {
          approachingView = v;
        }
      }
    }
    if (viewThatShouldStick != null) {
      stickyViewTopOffset = approachingView == null ? MIN_STICK_TOP : Math.min(MIN_STICK_TOP,
          getTopForViewRelativeOnlyChild(approachingView) - getScrollY()
              + (clippingToPadding ? 0 : getPaddingTop()) - viewThatShouldStick.getHeight());
      if (viewThatShouldStick != currentlyStickingView) {
        if (currentlyStickingView != null) {
          stopStickingCurrentlyStickingView();
        }
        // only compute the left offset when we start sticking.
        stickyViewLeftOffset = getLeftForViewRelativeOnlyChild(viewThatShouldStick);
        startStickingView(viewThatShouldStick);
      }
    } else if (currentlyStickingView != null) {
      stopStickingCurrentlyStickingView();
    }
  }

  private void startStickingView(View viewThatShouldStick) {
    currentlyStickingView = viewThatShouldStick;
    if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)) {
      hideView(currentlyStickingView);
    }
    if (((String) currentlyStickingView.getTag()).contains(FLAG_NONCONSTANT)) {
      post(invalidateRunnable);
    }
  }

  private void stopStickingCurrentlyStickingView() {
    if (getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)) {
      showView(currentlyStickingView);
    }
    currentlyStickingView = null;
    removeCallbacks(invalidateRunnable);
  }

  /**
   * Notify that the sticky attribute has been added or removed from one or
   * more views in the View hierarchy
   */
  public void notifyStickyAttributeChanged() {
    notifyHierarchyChanged();
  }

  private void notifyHierarchyChanged() {
    if (currentlyStickingView != null) {
      stopStickingCurrentlyStickingView();
    }
    stickyViews.clear();
    findStickyViews(getChildAt(0));
    doTheStickyThing();
    invalidate();
  }

  private void findStickyViews(View v) {
    if (v instanceof ViewGroup) {
      ViewGroup vg = (ViewGroup) v;
      for (int i = 0; i < vg.getChildCount(); i++) {
        String tag = getStringTagForView(vg.getChildAt(i));
        if (tag != null && tag.contains(STICKY_TAG)) {
          stickyViews.add(vg.getChildAt(i));
        } else if (vg.getChildAt(i) instanceof ViewGroup) {
          findStickyViews(vg.getChildAt(i));
        }
      }
    } else {
      String tag = (String) v.getTag();
      if (tag != null && tag.contains(STICKY_TAG)) {
        stickyViews.add(v);
      }
    }
  }

  private String getStringTagForView(View v) {
    Object tagObject = v.getTag();
    return String.valueOf(tagObject);
  }

  private void hideView(View v) {
    if (Build.VERSION.SDK_INT >= 11) {
      v.setAlpha(0);
    } else {
      AlphaAnimation anim = new AlphaAnimation(1, 0);
      anim.setDuration(0);
      anim.setFillAfter(true);
      v.startAnimation(anim);
    }
  }

  private void showView(View v) {
    if (Build.VERSION.SDK_INT >= 11) {
      v.setAlpha(1);
    } else {
      AlphaAnimation anim = new AlphaAnimation(0, 1);
      anim.setDuration(0);
      anim.setFillAfter(true);
      v.startAnimation(anim);
    }
  }

  /**
   * 设置悬浮高度
   * @param height
   */
  public void setStickTop(int height) {
    MIN_STICK_TOP = height;
  }

  /**
   * 解决vviewpager在scrollview滑动冲突的问题
   */
  // 滑动距离及坐标
  private float xDistance, yDistance, xLast, yLast;

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        xDistance = yDistance = 0f;
        xLast = ev.getX();
        yLast = ev.getY();
        break;
      case MotionEvent.ACTION_MOVE:
        final float curX = ev.getX();
        final float curY = ev.getY();

        xDistance += Math.abs(curX - xLast);
        yDistance += Math.abs(curY - yLast);
//        com.ihaveu.utils.Log.i("test", "curx:"+curX+",cury:"+curY+",xlast:"+xLast+",ylast:"+yLast);
//        xLast = curX;
//        yLast = curY;

        if (xDistance > yDistance) {
          return false;
        }

    }
    return super.onInterceptTouchEvent(ev);
  }
}

接下来是调用自定义控件了,用到两个关键的方法。StatusBarUtil.setTranslucentForImageView(MainActivity.this, 0, title)和llTitle.setBackgroundColor(Color.argb((int) alpha, 227, 29, 26))分别设置状态栏和标题栏的颜色。       

 package com.xiaoyuan;

import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.jaeger.library.StatusBarUtil;

public class MainActivity extends AppCompatActivity implements View.OnClickListener, StickyScrollView.OnScrollChangedListener {

  TextView oneTextView, twoTextView;
  private StickyScrollView stickyScrollView;
  private int height;
  private LinearLayout llContent;
  private RelativeLayout llTitle;
  private FrameLayout frameLayout;
  private TextView title;

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

  /**
   * 初始化View
   */
  private void initView() {
    stickyScrollView = (StickyScrollView) findViewById(R.id.scrollView);
    frameLayout = (FrameLayout) findViewById(R.id.tabMainContainer);
    title = (TextView) findViewById(R.id.title);
    oneTextView = (TextView) findViewById(R.id.infoText);
    llContent = (LinearLayout) findViewById(R.id.ll_content);
    llTitle = (RelativeLayout) findViewById(R.id.ll_good_detail);
    oneTextView.setOnClickListener(this);
    twoTextView = (TextView) findViewById(R.id.secondText);
    twoTextView.setOnClickListener(this);

    stickyScrollView.setOnScrollListener(this);
    StatusBarUtil.setTranslucentForImageView(MainActivity.this, 0, title);
    FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) llTitle.getLayoutParams();
    params.setMargins(0, getStatusHeight(), 0, 0);
    llTitle.setLayoutParams(params);

    //默认设置一个Frg
    getSupportFragmentManager().beginTransaction().replace(R.id.tabMainContainer, Fragment.newInstance()).commit();
  }

  /**
   * 获取状态栏高度
   *
   * @return
   */
  private int getStatusHeight() {
    int resourceId = MainActivity.this.getResources().getIdentifier("status_bar_height", "dimen", "android");
    return getResources().getDimensionPixelSize(resourceId);

  }

  @Override
  public void onClick(View v) {
    if (v.getId() == R.id.infoText) {
      getSupportFragmentManager().beginTransaction().replace(R.id.tabMainContainer, Fragment.newInstance()).commit();
    } else if (v.getId() == R.id.secondText) {
      getSupportFragmentManager().beginTransaction().replace(R.id.tabMainContainer, Fragment1.newInstance()).commit();

    }
  }

  private void initListeners() {
    //获取内容总高度
    final ViewTreeObserver vto = llContent.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        height = llContent.getHeight();
        //注意要移除
        llContent.getViewTreeObserver()
            .removeGlobalOnLayoutListener(this);

      }
    });

    //获取Fragment高度
    ViewTreeObserver viewTreeObserver = frameLayout.getViewTreeObserver();
    viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        height = height - frameLayout.getHeight();
        //注意要移除
        frameLayout.getViewTreeObserver()
            .removeGlobalOnLayoutListener(this);
      }
    });

    //获取title高度
    ViewTreeObserver viewTreeObserver1 = llTitle.getViewTreeObserver();
    viewTreeObserver1.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        height = height - llTitle.getHeight() - getStatusHeight();//计算滑动的总距离
        stickyScrollView.setStickTop(llTitle.getHeight() + getStatusHeight());//设置距离多少悬浮
        //注意要移除
        llTitle.getViewTreeObserver()
            .removeGlobalOnLayoutListener(this);
      }
    });

  }

  @Override
  public void onScrollChanged(int l, int t, int oldl, int oldt) {
    if (t <= 0) {
      llTitle.setBackgroundColor(Color.argb((int) 0, 255, 255, 255));

    } else if (t > 0 && t <= height) {
      float scale = (float) t / height;
      int alpha = (int) (255 * scale);
      llTitle.setBackgroundColor(Color.argb((int) alpha, 227, 29, 26));//设置标题栏的透明度及颜色
      StatusBarUtil.setTranslucentForImageView(MainActivity.this, alpha, title);//设置状态栏的透明度
    } else { StatusBarUtil.setTranslucentForImageView(MainActivity.this, 0, title);
      llTitle.setBackgroundColor(Color.argb((int) 255, 227, 29, 26));
      StatusBarUtil.setTranslucentForImageView(MainActivity.this, 255, title);
    }
  }
}

最后demo下载:demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
沉浸状态栏
android 沉浸式状态栏、android 沉浸状态栏、android4.4状态栏沉浸、android创意实例详解、沉浸式状态栏,以便于您获取更多的相关知识。

时间: 2024-11-02 06:49:53

详解Android中的沉浸式状态栏效果实例_Android的相关文章

详解Android中图片的三级缓存及实例

详解Android中图片的三级缓存及实例 为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响 特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知 所以提出三级缓存策略,通过网络.本地.内存三级缓存图片,来减少不必要的网络交互,避免浪费流量

详解Android中Fragment的两种创建方式_Android

fragment是Activity中用户界面的一个行为或者是一部分.你可以在一个单独的Activity上把多个Fragment组合成为一个多区域的UI,并且可以在多个Activity中再使用.你可以认为fragment是activity的一个模块零件,它有自己的生命周期,接收它自己的输入事件,并且可以在Activity运行时添加或者删除. 两个概念:Fragment.宿主 fragment的生命周期直接受其宿主activity的生命周期的影响.例如,一旦activity被暂停,它里面所有的fra

详解Android中Runtime解决屏幕旋转问题(推荐)_Android

前言 大家或许在iOS程序开发中经常遇到屏幕旋转问题,比如说希望指定的页面进行不同的屏幕旋转,但由于系统提供的方法是导航控制器的全局方法,无法随意的达到这种需求.一般的解决方案是继承UINavrgationViewController,重写该类的相关方法,这样虽然也能解决问题,但是在重写的过程中至少产生两个多余的文件和不少的代码,这显然不是我们想要的.下面就使用一种较底层的方法解决这个问题. 基本原理 动态的改变UINavrgationViewController的全局方法,将我们自己重写的su

详解Android中Intent对象与Intent Filter过滤匹配过程_Android

如果对Intent不是特别了解,可以参见博文<详解Android中Intent的使用方法>,该文对本文要使用的action.category以及data都进行了详细介绍.如果想了解在开发中常见Intent的使用,可以参见<Android中Intent习惯用法>. 本文内容有点长,希望大家可以耐心读完. 本文在描述组件在manifest中注册的Intent Filter过滤器时,统一用intent-filter表示. 一.概述 我们知道,Intent是分两种的:显式Intent和隐式

详解Android中的Service

Service简介: Service是被设计用来在后台执行一些需要长时间运行的操作. Android由于允许Service在后台运行,甚至在结束Activity后,因此相对来说,Service相比Activity拥有更高的优先级. 创建Service: 要创建一个最基本的Service,需要完成以下工作:1)创建一个Java类,并让其继承Service 2)重写onCreate()和onBind()方法 其中,onCreate()方法是当该Service被创建时执行的方法,onBind()是该S

详解Android中Handler的内部实现原理_Android

本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文<详解Android中Handler的使用方法>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功

详解 Android中Libgdx使用ShapeRenderer自定义Actor解决无法接收到Touch事件的问题

详解 Android中Libgdx使用ShapeRenderer自定义Actor解决无法接收到Touch事件的问题 今天在项目中实现了一个效果,主要是画一个圆.为了后续使用方便,将这个圆封装在一个自定义Actor(CircleActot)中,后续想显示一个圆的时候,只要创建一个CircleActor中即可. 部分代码如下所示: package com.ef.smallstar.unitmap.widget; import android.content.res.Resources; import

Android 4.4以上&quot;沉浸式&quot;状态栏效果的实现方法_Android

什么是沉浸式状态栏? 沉浸式状态栏意思指状态栏的颜色随着软件颜色而改变,使状态栏和软件颜色保持一致,沉浸其中!当我们打开应用程序时,不会再因为看到应用程序和状态栏的黑边相隔开而感到十分难看.沉浸式状态栏由于其能给用户群体带来极佳的用户体验,已经在越来越多的应用上得到了体现. 实现原理       从4.4后系统增加了透明状态栏的特性WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS一旦添加上这个属性后,那么布局中的内容DecorView就会自动填

Android 实现沉浸式状态栏的方法_Android

沉浸式状态栏的来源就是很多手机用的是实体按键,没有虚拟键,于是开了沉浸模式就只有状态栏消失了.于是沉浸模式成了沉浸式状态栏. 我们先来看下具体的效果 开启沉浸模式后,状态栏消失,从顶部向下滑动,状态栏出现,退出沉浸模式,状态栏也出现了. 我们的代码基于前一篇文章.首先是两个开启沉浸模式和关闭沉浸模式的函数 @SuppressLint("NewApi") public static void hideSystemUI(View view) { view.setSystemUiVisibi