Android 二次最佳化個人封裝新聞可滑動標題欄

25minutes發表於2021-09-09

   小菜前段時間根據超多 star 的 自己修改封裝了仿網易頂部滑動標題欄 滑動內容可以是文字也可以是網路圖示,並整理了兩篇小部落格:

      因小菜自己封裝的 TabSlideLayout 在滑動過程中沒有回彈的動畫效果,而 預設的滑動過程中也沒有動畫效果,而小菜技術太渣,所以只能照葫蘆畫瓢,按照 FlycoTabLayout 中的 CommonTabLayout 樣式,二次最佳化一下自己的 TabSlideLayout。

      小菜修改封裝的 TabSlideLayout 是一個不限制欄目個數,超過螢幕範圍可滑動切換的頂部 Tab 佈局,item 的內容可以是文字也可以是圖片,並且支援對文字和圖片對繪色。


小菜測試步驟如下:

  1. 在 attrs 中新增如下屬性,分別時是否回彈效果/是否回彈/回彈效果時長;並在 TabSlideLayout 中新增相應的 get/set 方法,從而方便在 Java 程式碼中動態設定;

<attr name="tl_indicator_anim_enable"/><attr name="tl_indicator_anim_duration"/><attr name="tl_indicator_bounce_enable"/>
mIndicatorAnimEnable = ta.getBoolean(R.styleable.SlidingTabLayout_tl_indicator_anim_enable, true);
mIndicatorBounceEnable = ta.getBoolean(R.styleable.SlidingTabLayout_tl_indicator_bounce_enable, true);
mIndicatorAnimDuration = ta.getInt(R.styleable.SlidingTabLayout_tl_indicator_anim_duration, -1);public void setIndicatorAnimDuration(long indicatorAnimDuration) {    this.mIndicatorAnimDuration = indicatorAnimDuration;
}public void setIndicatorAnimEnable(boolean indicatorAnimEnable) {    this.mIndicatorAnimEnable = indicatorAnimEnable;
}public void setIndicatorBounceEnable(boolean indicatorBounceEnable) {    this.mIndicatorBounceEnable = indicatorBounceEnable;
}public long getIndicatorAnimDuration() {    return mIndicatorAnimDuration;
}public boolean isIndicatorAnimEnable() {    return mIndicatorAnimEnable;
}public boolean isIndicatorBounceEnable() {    return mIndicatorBounceEnable;
}
  1. 照葫蘆畫瓢,繼承屬性動畫的 ValueAnimator,並實現基本動畫效果;

@Overridepublic void onAnimationUpdate(ValueAnimator animation) {
    View currentTabView = mTabsContainer.getChildAt(this.mCurrentTab);
    IndicatorPoint p = (IndicatorPoint) animation.getAnimatedValue();
    mIndicatorRect.left = (int) p.left;
    mIndicatorRect.right = (int) p.right;    if (mIndicatorWidth < 0) {   //indicatorWidth小於0時,原jpardogo's PagerSlidingTabStrip

    } else {//indicatorWidth大於0時,圓角矩形以及三角形
        float indicatorLeft = p.left + (currentTabView.getWidth() - mIndicatorWidth) / 2;

        mIndicatorRect.left = (int) indicatorLeft;
        mIndicatorRect.right = (int) (mIndicatorRect.left + mIndicatorWidth);
    }
    invalidate();
}
  1. 核心重要的第一步,調整 onDraw() 方法中繪製底部選中狀態的判斷處理,只需在第一次繪製即可,以後的滑動和選中狀態無需繪製,這樣可以防止在選擇頂部滑動過程中跳動;

private boolean mIsFirstDraw = true;//draw indicator lineif (mIndicatorAnimEnable) {   if (mIsFirstDraw) {
       mIsFirstDraw = false;
       calcIndicatorRect();
   }
} else {
   calcIndicatorRect();
}
  1. 核心重要第二步,重寫 setCurrentTab 方法,分別獲取當前選中位置的座標和 item 陣列位置以及下一次選中位置的座標和 item 陣列位置;

public void setCurrentTab(int currentTab, boolean smoothScroll) {
    mLastTab = this.mCurrentTab;    this.mCurrentTab = currentTab;
    updateTabSelection(currentTab);    if (mFragmentChangeManager != null) {
        mFragmentChangeManager.setFragments(currentTab);
    }    if (mIndicatorAnimEnable) {
        calcOffset();
    } else {
        invalidate();
    }
}
  1. 核心重要第三步,設定 TabSlideLayout item 的點選事件或 ViewPager 滑動時的效果,若只需要 item 點選時回彈效果,則直接設定 item 佈局的點選事件既可以,呼叫步驟四的方法;若設定 ViewPager 滑動時回彈效果,在 onPageSelected 方法中呼叫步驟四方法,並重寫 onPageScrolled 方法;

@Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {    /**
     * position:當前View的位置
     * mCurrentPositionOffset:當前View的偏移量比例.[0,1)
     */
    scrollToCurrentTab();
    invalidate();
}

Tips: 若 ViewPager 的 onPageSelected 呼叫步驟四方法後,在 item 點選時可去掉步驟四方法的呼叫,否則點選時,會回彈兩次,效果不佳。

  1. 以上基本可以實現滑動過程和點選過程中的回彈動畫效果,但是有個效果不佳的地方是:中間內容滾動過程中,文字切換居中渲染顏色時很生硬,效果不佳。TabSlideLayout 方式 item 個數沒有限制,整個內容超過螢幕寬度,滑動過程中內容位置會變化,這可能也是 中沒有新增動畫效果的原因之一。小菜嘗試的解決辦法有兩個,第一個是在呼叫 scrollTo 方法時新增延遲;第二個是在渲染文字顏色過程中新增延遲;小菜個人更傾向於後者,給人感覺會順暢一些,但依舊並非是最佳的解決方法,仍有待研究。

    預設不支援回彈效果

// 方案一:new Handler().postDelayed(new Runnable(){    public void run() {
        scrollTo(tempX, 0);
    }
}, mIndicatorBounceEnable ? 600 : 250);// 方案二:if (tab_title != null) {   new Handler().postDelayed(new Runnable(){       public void run() {
           tab_title.setTextColor(isSelect ? mTextSelectColor : mTextUnselectColor);            if (mTextBold == TEXT_BOLD_WHEN_SELECT) {
                tab_title.getPaint().setFakeBoldText(isSelect);
            }
        }
    }, mIndicatorBounceEnable ? 400 : 250);
}

      小菜再一次體會到,最佳化與封裝是一個耗時、用心的漫長過程,需要不斷的測試和嘗試。小菜現在的解決方案也並非最佳效果,不合理的地方還請各位多多指教。
      

圖片描述

ACE01_4.jpg



作者:阿策神奇


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4328/viewspace-2822343/,如需轉載,請註明出處,否則將追究法律責任。

相關文章