/* * Copyright (C) 2017 Drakeet <drakeet.me@gmail.com> * * This file is part of rebase-android * * rebase-android is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * rebase-android is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rebase-android. If not, see <http://www.gnu.org/licenses/>. */ package com.drakeet.rebase.widget; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import com.drakeet.rebase.R; import java.util.ArrayList; import java.util.List; /** * @author drakeet */ public class CompactTabLayout extends TabLayout { private final int INDICATOR_HEIGHT; private int height; private int tabCount, lastTabCount; private float indicatorLeftOffset; private int selectedPosition; private Paint hoverPaint; private List<TextView> tabViews; private int[] tabWidths; public CompactTabLayout(Context context) { this(context, null); } public CompactTabLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } @SuppressLint("PrivateResource") public CompactTabLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout, defStyleAttr, R.style.Widget_Design_TabLayout); int tabBackgroundResId = a.getResourceId(R.styleable.TabLayout_tabBackground, 0); a.recycle(); INDICATOR_HEIGHT = dpToPx(2); hoverPaint = new Paint(); hoverPaint.setColor(ContextCompat.getColor(context, tabBackgroundResId)); hoverPaint.setStyle(Paint.Style.FILL); tabViews = new ArrayList<>(tabCount); } @Override public void setupWithViewPager(@Nullable ViewPager viewPager) { super.setupWithViewPager(viewPager); if (viewPager != null) { viewPager.addOnPageChangeListener(new HoverOnPageChangeListener(this)); } assert viewPager != null; lastTabCount = tabCount; tabCount = viewPager.getAdapter().getCount(); tabWidths = new int[tabCount]; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); height = h; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureTabWidths(); } private void measureTabWidths() { for (int i = 0; i < tabCount; i++) { int tabWidth = ((LinearLayout) getChildAt(0)).getChildAt(i).getWidth(); if (tabWidth == 0) { break; } tabWidths[i] = tabWidth; } } @Override protected void onFinishInflate() { super.onFinishInflate(); } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (tabCount == 0) { return; } if (lastTabCount != tabCount) { measureTabWidths(); lastTabCount = tabCount; } int offset = 0; for (int i = 0; i < selectedPosition; i++) { offset += tabWidths[i]; } int tabWidth = tabWidths[selectedPosition]; int indicatorWidth = tabWidth / 5; canvas.translate(offset, 0); // 移动到第 N 个 tab 起点 canvas.translate(indicatorLeftOffset * tabWidth, 0); // 第 N 个 tab 当前滑动相对自身偏移量 canvas.save(); canvas.translate(0, height - INDICATOR_HEIGHT); // 移动到底部下划线位置 int halfHoverWidth = tabWidth / 2 - indicatorWidth / 2; canvas.drawRect(-2, 0, halfHoverWidth, INDICATOR_HEIGHT + 9, hoverPaint); canvas.translate((tabWidth / 2 + indicatorWidth / 2), 0); canvas.drawRect(0, 0, halfHoverWidth * 2, INDICATOR_HEIGHT + 9, hoverPaint); canvas.restore(); } @Override public void addTab(@NonNull Tab tab, boolean setSelected) { TextView textView = (TextView) View.inflate(getContext(), R.layout.tab_text, null); textView.setText(tab.getText()); /* Selected first one by default */ if (tabViews.isEmpty()) { renderSelectedText(textView); } tab.setCustomView(textView); tabViews.add(textView); super.addTab(tab, setSelected); } public class HoverOnPageChangeListener extends TabLayoutOnPageChangeListener { HoverOnPageChangeListener(TabLayout tabLayout) { super(tabLayout); } @Override public void onPageSelected(int position) { super.onPageSelected(position); for (int i = 0; i < tabCount; i++) { final TextView textView = tabViews.get(i); if (i == position) { renderSelectedText(textView); } else { renderNormalText(textView); } } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { super.onPageScrolled(position, positionOffset, positionOffsetPixels); ViewCompat.postInvalidateOnAnimation(CompactTabLayout.this); indicatorLeftOffset = positionOffset; selectedPosition = position; } } private void renderSelectedText(TextView textView) { // textView.setTextSize(14); textView.setTypeface(null, Typeface.BOLD); } private void renderNormalText(TextView textView) { // textView.setTextSize(14); textView.setTypeface(null, Typeface.NORMAL); } int dpToPx(int dps) { return Math.round(getResources().getDisplayMetrics().density * dps); } }