1. 程式人生 > >實現淘寶商品詳情頁toolbar 漸變、狀態列沉浸

實現淘寶商品詳情頁toolbar 漸變、狀態列沉浸

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">	</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">本人做的專案是某傳統行業的一款電商產品,專案中有的商品詳情頁需要和淘寶商品詳情頁類似的效果,就是實現toolbar和狀態列 更加頁面的滾動實習透明度的漸變</span>

其實和某寶還有點差距 ,畢竟 我們是在H5頁面去做的這種效果,接下來我說說具體實現。

這個h5頁 只有title bar (toolbar)是本地 然後下面是webview,通過重寫webview 的onscrollChangedListener() 方法。而onscrollChangedListener 的許可權是protected 所有隻有繼承Webview 如下

public class CustomWebview extends WebView {
  public ScrollChangeListener mChangeListener;

  public CustomWebview(Context context) {
    super(context);
  }

  public CustomWebview(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public CustomWebview(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  public CustomWebview(Context context, AttributeSet attrs, int defStyleAttr,
                       int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }

  @Override
  protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    super.onScrollChanged(l, t, oldl, oldt);

    if (mChangeListener != null) {
      mChangeListener.onScrollChanged(l, t, oldl, oldt);
    }
  }

  public void setOnScrollChangeListener(ScrollChangeListener listener) {
    mChangeListener = listener;

  }

  interface ScrollChangeListener {
    void onScrollChanged(int l, int t, int oldl, int oldt);
  }
}
然後 在fragment裡面例項化這個控制元件 ,因為我們的H5fragment是個基類 所以 就寫了一個空的 方法 供子類繼承重寫
  private void initWebViewScrollChangeListener() {
    if (mWebView instanceof CustomWebview) {
      ((CustomWebview) mWebView)
          .setOnScrollChangeListener(new CustomWebview.ScrollChangeListener() {
            @Override
            public void onScrollChanged(int l, int t, int oldl, int oldt) {
              onWebViewScrollChanged(l, t, oldl, oldt);//這個是fragment裡面的一個空的方法

            }
          });
    }
  }

這兒解釋一下 我們架構 都是一個activity 巢狀一個fragment,所有的業務邏輯都在fragment裡面去做。toolbar的操作是在activity裡面進行的

activity的程式碼 一些說明我就寫在程式碼的註釋裡面了

public class ShadowH5Activity extends H5Activity {
  private ShadowH5Fragment mFlashBuyH5Fragment;
  private SystemBarTintManager mTintManager;
  private CommonTitleView mTitleView;// 雖然用的toolbar,但是在我們專案中他只是個容器,比如這個view就是 真正的一個有返回箭頭 和title的view

//可以自定義view的樣式 在這裡 我就是操作的這個view整體background的透明度 ,

  @Override
  protected void onCreate(Bundle onSaveInstanceState) {
    super.onCreate(onSaveInstanceState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//特別說明一下 沉浸式效果只有在sdk19以上的版本才有,以下的沒有
      showStatusBar();
    }
  }


  @Override
  public BaseFragment getFragment() {
    mFlashBuyH5Fragment = (ShadowH5Fragment) Fragment.instantiate(this,
        ShadowH5Fragment.class.getName(), getIntent().getExtras());
    return mFlashBuyH5Fragment;
  }

  @Override
  protected int getLayoutId() {
    return R.layout.shadow_tool_bar_h5_fragment_activity;//下面會有佈局的程式碼
  }



  @Override
  protected void customizeToolbar(Toolbar toolbar) {
    setToolbarMarginTop(toolbar);
  }

  /**
   *
   * @param alpha 0~255
   */ 背景的透明度
  public void setTitleViewBacGroundAlpha(int alpha) {
    if (mTitleView != null && mTitleView.getBackground() != null)
      mTitleView.getBackground().setAlpha(alpha);
  }

  @Override
  protected TitleContainer getMyTitleContainer() {
    mTitleView = CommonTitleView.newInstance(this);//自定義一個view 不過 我還是複用的老的view 在這個 我想拿到這個view的物件 所以重寫了
    return mTitleView;
  }

  /**
   *
   * @param alpha 0~1 控制title的透明度
   */
  public void setTitleAlpha(float alpha) {
    if (mTitleView != null && mTitleView.getTitle() != null) {
      mTitleView.getTitle().setAlpha(alpha);
    }
  }

  /**
   * 相容性問題
   * 
   * @param toolbar
   */
  @TargetApi(19)
  private void setToolbarMarginTop(Toolbar toolbar) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//前面說了,版本19以上 的會有狀太欄 漸變的效果,因為我吧整個頁面全屏之後
      RelativeLayout.LayoutParams params =// statusBar 就隱藏了,但是我會把它show出來 ,此時toolbar和statusbar就會重疊,所以我給toolbar設定
          new RelativeLayout.LayoutParams(toolbar.getLayoutParams());//marginTop的屬性,正好距離頂部一個statusbar的高度,=
      params.setMargins(0, getStatusBarHeight(), 0, 0);
      toolbar.setLayoutParams(params);
    }
  }


  @Override
  protected void initWindow() {重寫基類的方法 實現沉浸式statusbar 藍色的
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
      getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
      mTintManager = new SystemBarTintManager(this);
      mTintManager.setStatusBarTintEnabled(true);
      mTintManager.setTintColor(getResources().getColor(R.color.theme_blue));
    }

  }

  /**
   *
   * @return 狀態列高度
   */
  public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
      result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
  }

  public void setStatusBarAlpha(float statusBarAlpha) {
    if (mTintManager != null) {
      mTintManager.setTintAlpha(statusBarAlpha);
    }
  }

  /**
   * 隱藏狀態列
   */
  private void hideStatusBar() {
    WindowManager.LayoutParams attrs = getWindow().getAttributes();
    attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
    getWindow().setAttributes(attrs);
  }

  /**
   * 在全屏狀態下 顯示狀態列 (這種顯示出來的statusbar 不會導致整個頁面重繪)
   */
  private void showStatusBar() {
    WindowManager.LayoutParams attrs = getWindow().getAttributes();
    attrs.flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
    getWindow().setAttributes(attrs);
  }

}

xml佈局
<?xml version="1.0" encoding="utf-8"?>
<xxx.sliding.SlidingLayout
    android:id="@+id/sliding_pane_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/transparent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/default_background"/>

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@android:color/transparent"/>

    </RelativeLayout>

</xxx.sliding.SlidingLayout>
fragment  的程式碼如下
public class ShadowH5Fragment extends H5Fragment {
  private ShadowH5Activity mActivity;
  private static final float SCROLL_LIMIT = 300f;//滑動的距離
  private static final float sDEFAULT_ALPHA = 0;//預設的透明度

  @Override
  protected void onInflated(View contentView, Bundle savedInstanceState) {
    super.onInflated(contentView, savedInstanceState);
    initViewAlpha();
  }

  private void initViewAlpha() {
    if (getActivity() != null) {
      if (getActivity() instanceof ShadowH5Activity) {
        mActivity = (ShadowH5Activity) getActivity();
        mActivity.setTitleViewBacGroundAlpha((int) sDEFAULT_ALPHA);
        mActivity.setTitleAlpha(sDEFAULT_ALPHA);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
          mActivity.setStatusBarAlpha(sDEFAULT_ALPHA);
        }
      }
    }
  }

  @Override
  protected void onWebViewScrollChanged(int l, int t, int oldl, int oldt) {
    if (mActivity != null) {
      float castT = t;
      if (t < SCROLL_LIMIT) {
        float progress = (castT / SCROLL_LIMIT);
        progress = progress > 0 ? progress : 0;
        mActivity.setTitleViewBacGroundAlpha((int) (progress * 255));
        mActivity.setTitleAlpha(progress);
        mActivity.setStatusBarAlpha(progress);
      } else {
        mActivity.setTitleViewBacGroundAlpha(255);
        mActivity.setTitleAlpha(1);
        mActivity.setStatusBarAlpha(1);
      }

    }
  }
}