1. 程式人生 > >自定義View指示器讓指示器下劃線和文字對齊

自定義View指示器讓指示器下劃線和文字對齊

指示器的標籤分為兩種情形,一種是固定的一般3~5個標籤,另一種的移動的標籤;開發中經常會使用MD風格的TabLayout控制元件,這個控制元件什麼都很好,就是指示器的下滑線和文字不能夠對齊,這一點用過的同學都會知道的,我也在網上找了好久,也找到不到現成的,只好自己動手來寫一個啦!
先宣告一下,我是吧指示器標籤分兩種情形來寫的,固定和不固定,沒有合併,因為不是繼承同一個控制元件;另外要多看看TabLayout的原始碼,對於自定義這個控制元件很有好處,可以多多借鑑裡面的實現;
最下面可以下載程式碼,有問題可以留言交流;
說了這麼多先看看固定的標籤的效果:(Tab是固定的效果)

這裡寫圖片描述

這種效果實現的主要步驟思路:
因為這種標籤固定
1.首先要把所有的標籤文字全部放到一個容器裡面,寬度按個數均分,容器選水平LinearLayout


2.要關聯上一個ViewPager,並根據ViewPager的改變動態繪製指示器;
新增的程式碼如下:

      /**
     * 把資料集合設定成Tab標籤
     */
    public void setTabTitle(List<String> list) {
        if (list == null)
            return;
        mTabTitleList = list;//tab資料來源
        mTabCount = list.size();//tab的個數
        for (int i = 0; i < list.size(); i++) {
            String text = list.get(i);
            addView(generateText(text));//新增到容器中
} setTabOnClickListener();//設定點選事件 } /** * 自動生成TextView */ public TextView generateText(String text) { TextView textView = new TextView(getContext()); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSize); textView.setTextColor(mNormalColor); textView.setGravity(Gravity.CENTER); textView.setText(text); LinearLayout.LayoutParams lp = new
LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1); textView.setLayoutParams(lp); return textView; } /** * 每一個tab的點選事件 */ public void setTabOnClickListener() { if (mTabTitleList != null) { for (int i = 0; i < mTabCount; i++) { TextView textView = (TextView) getChildAt(i); textView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int index = indexOfChild(v); if (mViewPager != null) mViewPager.setCurrentItem(index, true); } }); } } }

上面程式碼很簡單就是把根據文字生成了TextView新增到LinearLayout裡面,並且均分;這一步設定好了以後,下面就開始關聯ViewPager;

    private RectF rectF = new RectF();//要繪製的矩形

    //我們是根據下面兩個引數來動態計算並繪製
    private int position;//ViewPager的裡面的position
    private float ration;//滑動的百分比

    /**
     * 關聯到ViewPager
     */
    public void setViewPager(ViewPager viewPager) {
        this.mViewPager = viewPager;
        this.mViewPager.addOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int pos) {
            }

            @Override
            public void onPageScrolled(int arg0, float arg1, int arg2) {
                position = arg0;
                ration = arg1;
                //這是為了找出指示器大概會滑動到那個Tab位置
                int pos = Math.round(arg0 + arg1);
                for (int i = 0; i < mTabCount; i++) {
                    TextView textView = (TextView) getChildAt(i);
                    if (i == pos) {
                        textView.setTextColor(mSelectedColor);
                    } else {
                        textView.setTextColor(mNormalColor);
                    }
                }
                //重繪
                postInvalidate();
            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

    }

這裡我們主要在onPageScrolled這個方法裡面去做一下更新的操作;其中
int pos = Math.round(arg0 + arg1);這一行程式碼是為了讓ViewPager切換到一半的是時候會顯示另一個tab被選中,這一點我也是從原始碼裡面看到的,這裡借鑑了一下;最後重新繪製;

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (mTabTitleList != null && mTabCount > 0) {
            String text = mTabTitleList.get(position);//拿到文字
            int textWidth = (int) mPaint.measureText(text);//測量一下寬度
            int perWidth = mViewWidth / mTabCount;//每個Tab的平均寬度

            rectF.set(
                    perWidth * (position + ration) + perWidth / 2 - textWidth / 2,
                    mViewHeight - mPagerIndicatorHeight,
                    perWidth * (position + ration) + perWidth / 2 + textWidth / 2,
                    mViewHeight);
            canvas.drawRect(rectF, mPaint);
        }
    }

這裡重寫一下dispatchDraw方法,在super.dispatchDraw方法裡面是繪製子View,等它們都繪製完畢之後,在進行指示器下面的繪製;這一步不要在onDraw方法裡面繪製,因為LinearLayout本身沒有什麼需要繪製的,只是孩子View需要繪製;即使你繪製了也會被dispatchDraw方法繪製的內容覆蓋;

固定標籤的指示器到這裡就就結束了,還有其他一些初始化的工作這裡不說了;下面我們在來看看不固定的情形,也就是標籤的個數比較多的情形;

這裡寫圖片描述

上面的情形在寫的過程中,大部分程式碼都是參照TabLayout的,先說一下思路吧:
因為這種標籤可以移動
1.首先要把所有的標籤文字全部放到一個容器裡面,寬度按個數均分,容器選HorizontalScrollView,裡面只能有一個View我選擇LinearLayout
2.要關聯上一個ViewPager,並根據ViewPager的改變動態繪製指示器;
先初始化了程式碼:

    public ViewPagerIndicator_Scroll(Context context, AttributeSet attrs) {
        super(context, attrs);
        setHorizontalScrollBarEnabled(false);// Disable the Scroll Bar
        initPaint();// 初始化畫筆
        /**
         * HorizontalScrollView只能包含一個子View
         */
        mLinearLayout = new LinearLayout(context);
        mLinearLayout.setOrientation(LinearLayout.HORIZONTAL);// 水平方向
        mLinearLayout.setLayoutParams(new HorizontalScrollView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        addView(mLinearLayout);//新增
    }

這裡要注意是讓setHorizontalScrollBarEnabled讓滾動條不可用,而且要新增一個孩子容器,
我們把這些標籤全部放到這個孩子容器裡面;
生成文字標籤的程式碼:

    public TextView generateText(String text) {
        TextView textView = new TextView(getContext());
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, mTextSize);
        textView.setTextColor(mNormalColor);
        textView.setGravity(Gravity.CENTER);
        textView.setText(text);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
                (int) mPaint.measureText(text), //寬度最直接使用畫筆測量出來的寬度
                LinearLayout.LayoutParams.MATCH_PARENT);
        lp.setMargins(dp2px(5), 0, dp2px(5), 0);//設定了左右間距,可以自己調整
        textView.setLayoutParams(lp);
        return textView;
    }

寬度我沒有使用Wrap_content那樣會有擠壓,這裡直接使用畫筆測量文字的寬度;
我們將生成的表面新增容器裡面之後,我們最後就是和ViewPager的聯動了

  public void setViewPager(ViewPager viewPager) {
        this.mViewPager = viewPager;
        this.mViewPager.addOnPageChangeListener(new OnPageChangeListener() {

            @Override
            public void onPageSelected(int pos) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }

            @Override
            public void onPageScrolled(int position, float positionOffset,
                                       int positionOffsetPixels) {
                int pos = Math.round(position + positionOffset);
                for (int i = 0; i < mTabCount; i++) {
                    TextView textView = (TextView) mLinearLayout.getChildAt(i);
                    if (i == pos) {
                        textView.setTextColor(mSelectedColor);
                    } else {
                        textView.setTextColor(mNormalColor);
                    }
                }
                setIndicatorPositionFromTabPosition(position, positionOffset);
                scrollTo(calculateScrollXForTab(position, positionOffset), 0);
            }
        });

    }

在onPageScrolled方法中思路內部是這樣的:
1,設定一下目前將要滑動到那個標籤,我們就並做高亮顯示;
2,根據ViewPager位置資訊改變繪製指示器的下劃線;
3,移動當前View的內容,為了更好的展示
這三個步驟,我全部 都是參照的TabLayout的原始碼;我是搞了半天怎麼也行;最後才想著看看人家的程式碼實現;這也算是我的經驗吧;直接使用了程式碼中的演算法後,出來效果就是內容滑動的和下劃線會對齊;就是這麼神奇!!!貼一下程式碼吧:

   public void setIndicatorPositionFromTabPosition(int position, float positionOffset) {
        mSelectedPosition = position;
        mSelectionOffset = positionOffset;
        updateIndicatorPosition();
    }

    private void updateIndicatorPosition() {
        View selectedTitle = mLinearLayout.getChildAt(mSelectedPosition);
        int left, right;

        if (selectedTitle != null && selectedTitle.getWidth() > 0) {
            left = selectedTitle.getLeft();
            right = selectedTitle.getRight();

            if (mSelectionOffset > 0f && mSelectedPosition < mLinearLayout.getChildCount() - 1) {
                // Draw the selection partway between the tabs
                View nextTitle = mLinearLayout.getChildAt(mSelectedPosition + 1);
                left = (int) (mSelectionOffset * nextTitle.getLeft() +
                        (1.0f - mSelectionOffset) * left);
                right = (int) (mSelectionOffset * nextTitle.getRight() +
                        (1.0f - mSelectionOffset) * right);
            }
        } else {
            left = right = -1;
        }
        setIndicatorPosition(left, right);
    }


    void setIndicatorPosition(int left, int right) {
        if (left != mIndicatorLeft || right != mIndicatorRight) {
            mIndicatorLeft = left;
            mIndicatorRight = right;
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    private int calculateScrollXForTab(int position, float positionOffset) {

        View selectedChild = mLinearLayout.getChildAt(position);
        View nextChild = position + 1 < getChildCount()
                ? getChildAt(position + 1)
                : null;
        int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;
        int nextWidth = nextChild != null ? nextChild.getWidth() : 0;

        // base scroll amount: places center of tab in center of parent
        int scrollBase = selectedChild.getLeft() + (selectedWidth / 2) - (getWidth() / 2);
        // offset amount: fraction of the distance between centers of tabs
        int scrollOffset = (int) ((selectedWidth + nextWidth) * 0.5f * positionOffset);

        return (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR)
                ? scrollBase + scrollOffset
                : scrollBase - scrollOffset;

    }

相關推薦

定義View指示器指示器劃線文字

指示器的標籤分為兩種情形,一種是固定的一般3~5個標籤,另一種的移動的標籤;開發中經常會使用MD風格的TabLayout控制元件,這個控制元件什麼都很好,就是指示器的下滑線和文字不能夠對齊,這一點用過的同學都會知道的,我也在網上找了好久,也找到不到現成的,只好自

Android定義View完美實現指示器位置隨進度變化的IndicateProgressView

該文章同步釋出在公眾號”LinminTech”上,請在本文最後掃碼關注,獲取更多精彩Android開發文章。 效果圖 需求 在平時開發過程中,UI經常要求實現如上圖所示的ProgressBar,但是Android系統自帶的ProgressBar

定義View(ListView)拉重新整理

下拉重新整理的操作流程: 1.使用者手指在ListView頁面按下並下拉 2.出現一個提示View在listView頂部 3.listView內容更新,頂部view顯示後隱藏 具體實現步驟 建立繼承Listview的RefreshListView,並新增頂部提示vi

Android 定義view(1) --- Attr、StyleTheme詳解

轉載:https://www.jianshu.com/p/dd79220b47dd 概念說明:       Attr:屬性,風格樣式的最小單元;      Style:風格,它是一系列Attr的集合用以定義一個View

定義view,繪製階段進度progressBar,階段與圖片文字

  沒用seekbar或者progressbar原生控制元件,通過繪製實現。只講下有用的思想,無關屬性不解釋,也不用看。 主要看onDraw方法程式碼: 繪製背景線,canvas.drawRect線繪製了第一條線,因為需要漸變,可以看到canvas.drawPath是從第

定義控制元件TextView的drawableLeft與文字一起居中顯示

Drawable[] drawables = getCompoundDrawables();          if  (drawables != 

定義view,可拖拽進度吸附效果的圓形進度條

前言 最近接到一個需求,第一眼看到ui互動效果時,瞬間想對產品小哥說“尼瑪,這麼會玩,你咋不上天”。確認了具體互動細節,喝了兩口農夫三拳,開始了兩耳不聞窗外事,一心只想擼程式碼的過程。 先上ui效果 說明: 外圈弧形上面是進度的標記點,預設在12點位置,也是

定義控制元件EditText的drawableLeft與文字一起居中顯示

如上圖  專案中要求的顯示效果是serch圖片和EditText的文字一起在中間位置。 嘗試設定了很多EditText均不能實現滿意的效果,其中: android:drawableLeft="" android:drawableRight=""設定的位置均在兩邊,不能滿足

btn劃線 字型左

    NSString *appleNum = [NSString stringWithFormat:@"%@",ObjectToNumber(model.amount)];     // 下劃線     NSDictiona

DIV+CSS 同一行的圖片文字【轉】

在div+css佈局中,如果一行(或一個DIV)內容中有圖片和文字的話,圖片和文字往往會一個在上一個在下,這是一個新手都會遇到問題,我的解決方法有三:  1.新增CSS屬性:vertical-align:middle; 程式碼: <style> a img{b

CSS 同一行的圖片文字

大家在做前端開發的時候那,經常會遇到img標籤和文字在同一行。 那麼我剛開始的時候那是利用的float浮動佈局解決的,定位佈局(相容性需要調整 不划算)下面給大家介紹一些其他的方法: 1.新增CSS屬性:vertical-align:middle; 程式碼:  &l

仿新浪微博首頁具有泥鰍蚯蚓滑動效果的可定義View的Android ViewPager指示器

說到蚯蚓滑動效果的指示器,市面上很常見 也許是普通的控制元件習以為常了,刷微博的時候看到微博首頁的設計覺得很有意思 於是在公司的蚯蚓滑動指示器的基礎上進行了加工,寫了這麼一個指示器,效果如下所示 程式碼不多,正當我竊喜的時候我突然發現遇到個坑,點選popupwindo

Android開發實戰2----圓點導航指示器(使用定義View實現)

一、專案概述 當我們使用viewpager進行圖片預覽時,底部一般情況都會出現圓點導航指示器,效果如圖所示: 二、不好的一種處理方式: 剛開始的情況下,我們會現在activity中先定義一個LinearLayout,然後對於這個LinearLayout進行增刪Imag

安卓定義View——網易顏色漸變效果指示器

一直想寫部落格來著,可惜直到現在才真正抽出時間。最近一直在研究網易新聞這個UI框架,發現了一些很值得借鑑的效果,當然,網上也不乏這方面的介紹。本文主要實現的指示器效果為字型顏色和大小漸變,廢話不多說獻上效果圖: 實現效果主要包括: 指示器背景可以根據使用者自己定製形

模仿微博ViewPage指示器滑動效果的定義View

效果圖 偶然看到在微博的最新版本看到發現介面和個人介面的幾個Tab滑動的指示器添加了動畫,看起來比以前的線條滑動看起來生動些。 實現 實現滑動是我們Android開發經常運用到的ViewPage 線條的滑動則是根據ViewPage

Android從零開搞系列:定義View(4)基本的定義ViewPager指示器+開源專案分析(上)

基本的自定義ViewPager的指示器 當然關於ViewPager指示器,如果只需要簡潔大方,那麼我們最簡單的方案就是使用TabLayout+ViewPager。 當然咱們也有很多非常不錯的開源框架可以選擇。 本次的記錄的內容就是

Banner+定義View+SmartRefreshLayout拉重新整理上拉載入更多

仿美團開源專案整體架構和首頁其實早就完成了,前段時間家裡各種事情搞得心力交瘁,停更了一段時間。甚至一度動搖繼續這個專案的決心,因為最近在學前端,在技術的深度和廣度之間一直糾結搖擺不定。一個聲音是繼續完成這個專案,把安卓玩的更深入一些;另一個聲音是趕緊學前端吧

定義View-剪

Canvas 提供了剪下區的功能,剪下區可以是一個 Rect 或者是一個 Path,兩個剪下區還能進 行圖形運算,得到更加複雜的剪下區。我們來看看相關的方法: public boolean clipRect(Rect rect) public boolean clipRect(Rect

定義View——仿騰訊TIM拉重新整理View

一 概述 自定義 View 是 Android 開發裡面的一個大學問。偶然間看到 TIM 郵箱介面的重新整理 View 還挺好玩的,於是就自己動手實現了一個,先看看 TIM 裡邊的效果圖: 二 需求分析 看到上面的動圖,大概也知道我們需要實現的功能: 根據拖動的進度來移動小球的位

Android定義View之(拉重新整理+側滑刪除)

以前專案中用到了一個放qq的側滑刪除的效果,結果github上一搜就copy了一個,不得不說大神們寫的真心牛逼,那個時候呢看到一個東西能用就可以了,也不管怎麼實現的,現在反過來一看,原來自定義還可以這麼玩,當然,前面專案中也因此出現了一個bug,就是我使用的是P