Java tutorial
/* * Copyright 2014 Chris Banes * Copyright 2016 Christophe Beyls * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.linuxtage.companion.widgets; import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; import android.os.Build; import android.support.annotation.ColorInt; import android.support.annotation.IdRes; import android.support.annotation.LayoutRes; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.TextView; import at.linuxtage.companion.R; /** */ public class SlidingTabLayout extends HorizontalScrollView { public interface TabListener { void onTabSelected(int pos); void onTabReSelected(int pos); } private static final int[][] TAB_COLOR_STATES = new int[][] { SELECTED_STATE_SET, EMPTY_STATE_SET }; private int mTitleOffset; private int mTabViewLayoutId; private int mTabViewTextViewId; private boolean mDistributeEvenly; private ColorStateList mTextColor; ViewPager mViewPager; PagerAdapter mAdapter; private final InternalViewPagerListener mPageChangeListener = new InternalViewPagerListener(); private final PagerAdapterObserver mPagerAdapterObserver = new PagerAdapterObserver(); TabListener mTabListener; final SlidingTabStrip mTabStrip; public SlidingTabLayout(Context context) { this(context, null); } public SlidingTabLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // Disable the Scroll Bar setHorizontalScrollBarEnabled(false); // Make sure that the Tab Strips fills this View setFillViewport(true); mTabStrip = new SlidingTabStrip(context); addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlidingTabLayout, defStyle, R.style.SlidingTabLayout); mTabStrip.setSelectedIndicatorHeight( a.getDimensionPixelSize(R.styleable.SlidingTabLayout_indicatorHeight, 0)); mTabStrip.setSelectedIndicatorColor(a.getColor(R.styleable.SlidingTabLayout_indicatorColor, 0)); mTextColor = a.getColorStateList(R.styleable.SlidingTabLayout_textColor); if ((mTextColor != null) && a.hasValue(R.styleable.SlidingTabLayout_selectedTextColor)) { setTabTextColors(mTextColor.getDefaultColor(), a.getColor(R.styleable.SlidingTabLayout_selectedTextColor, 0)); } setContentInsetStart(a.getDimensionPixelSize(R.styleable.SlidingTabLayout_contentInsetStart, 0)); setDistributeEvenly(a.getBoolean(R.styleable.SlidingTabLayout_distributeEvenly, false)); a.recycle(); } public void setContentInsetStart(int offset) { mTitleOffset = offset; mTabStrip.setPadding(offset, 0, 0, 0); } public void setDistributeEvenly(boolean distributeEvenly) { mDistributeEvenly = distributeEvenly; } /** * Sets the color to be used for indicating the selected tab. * This will override the style color. */ public void setSelectedTabIndicatorColor(@ColorInt int color) { mTabStrip.setSelectedIndicatorColor(color); } public void setSelectedTabIndicatorHeight(int height) { mTabStrip.setSelectedIndicatorHeight(height); } public void setTabTextColors(ColorStateList color) { mTextColor = color; } public void setTabTextColors(@ColorInt int normalColor, @ColorInt int selectedColor) { mTextColor = createColorStateList(normalColor, selectedColor); } private static ColorStateList createColorStateList(int defaultColor, int selectedColor) { final int[] colors = new int[] { selectedColor, defaultColor }; return new ColorStateList(TAB_COLOR_STATES, colors); } /** * Set the custom layout to be inflated for the tab views. * * @param layoutResId Layout id to be inflated * @param textViewId id of the {@link TextView} in the inflated view */ public void setCustomTabView(@LayoutRes int layoutResId, @IdRes int textViewId) { mTabViewLayoutId = layoutResId; mTabViewTextViewId = textViewId; } public void setTabListener(TabListener tabListener) { mTabListener = tabListener; } /** * Sets the associated view pager. The ViewPager must have an adapter set. * The SlidingTabLayout will then listen for changes and update the tabs automatically. */ public void setViewPager(ViewPager viewPager) { if (mViewPager != null) { mViewPager.removeOnPageChangeListener(mPageChangeListener); mAdapter.unregisterDataSetObserver(mPagerAdapterObserver); } if (viewPager != null) { PagerAdapter adapter = viewPager.getAdapter(); if (adapter == null) { throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set"); } mViewPager = viewPager; mAdapter = adapter; mPageChangeListener.reset(); viewPager.addOnPageChangeListener(mPageChangeListener); adapter.registerDataSetObserver(mPagerAdapterObserver); } else { mViewPager = null; mAdapter = null; } notifyDataSetChanged(); } void notifyDataSetChanged() { mTabStrip.removeAllViews(); if (mViewPager != null) { populateTabStrip(); } } static void setSelectedCompat(View view, boolean selected) { final boolean becomeSelected = selected && !view.isSelected(); view.setSelected(selected); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN && becomeSelected) { // Pre-JB we need to manually send the TYPE_VIEW_SELECTED event view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); } } private void populateTabStrip() { final int adapterCount = mAdapter.getCount(); final View.OnClickListener tabClickListener = new TabClickListener(); final LayoutInflater inflater = LayoutInflater.from(getContext()); final int currentItem = mViewPager.getCurrentItem(); for (int i = 0; i < adapterCount; i++) { View tabView; TextView tabTitleView; if (mTabViewLayoutId != 0) { // If there is a custom tab view layout id set, try and inflate it tabView = inflater.inflate(mTabViewLayoutId, mTabStrip, false); tabTitleView = tabView.findViewById(mTabViewTextViewId); if (tabTitleView == null) { tabTitleView = (TextView) tabView; } } else { // Inflate our default tab layout tabView = inflater.inflate(R.layout.widget_sliding_tab_layout_text, mTabStrip, false); tabTitleView = (TextView) tabView; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { // Emulate Roboto Medium in previous Android versions tabTitleView.setTypeface(Typeface.DEFAULT_BOLD); } } if (mTextColor != null) { tabTitleView.setTextColor(mTextColor); } if (mDistributeEvenly) { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams(); lp.width = 0; lp.weight = 1; } tabTitleView.setText(mAdapter.getPageTitle(i)); tabView.setFocusable(true); tabView.setOnClickListener(tabClickListener); mTabStrip.addView(tabView); if (i == currentItem) { setSelectedCompat(tabView, true); } } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (mViewPager != null) { scrollToTab(mViewPager.getCurrentItem(), 0); } announceSelectedTab(hasWindowFocus()); } @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); announceSelectedTab(hasWindowFocus); } private void announceSelectedTab(boolean hasWindowFocus) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && hasWindowFocus) { getHandler().post(new Runnable() { @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Override public void run() { if (mViewPager != null && mAdapter != null && mAdapter.getCount() > 0) { CharSequence pageTitle = mAdapter.getPageTitle(mViewPager.getCurrentItem()); if (!TextUtils.isEmpty(pageTitle)) { announceForAccessibility(pageTitle); } } } }); } } void scrollToTab(int tabIndex, float positionOffset) { final int tabStripChildCount = mTabStrip.getChildCount(); if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { return; } View selectedChild = mTabStrip.getChildAt(tabIndex); if (selectedChild != null) { int targetScrollX = selectedChild.getLeft() + Math.round(positionOffset * selectedChild.getWidth()) - mTitleOffset; scrollTo(targetScrollX, 0); } } class InternalViewPagerListener implements ViewPager.OnPageChangeListener { private int mScrollState; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { int tabStripChildCount = mTabStrip.getChildCount(); if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { return; } mTabStrip.onViewPagerPageChanged(position, positionOffset); scrollToTab(position, positionOffset); } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; } @Override public void onPageSelected(int position) { if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { mTabStrip.onViewPagerPageChanged(position, 0f); scrollToTab(position, 0); } final int childCount = mTabStrip.getChildCount(); for (int i = 0; i < childCount; i++) { setSelectedCompat(mTabStrip.getChildAt(i), position == i); } } public void reset() { mScrollState = ViewPager.SCROLL_STATE_IDLE; } } class PagerAdapterObserver extends DataSetObserver { @Override public void onChanged() { notifyDataSetChanged(); } @Override public void onInvalidated() { notifyDataSetChanged(); } } class TabClickListener implements View.OnClickListener { @Override public void onClick(View v) { final int childCount = mTabStrip.getChildCount(); for (int i = 0; i < childCount; i++) { if (v == mTabStrip.getChildAt(i)) { final int previousPos = mViewPager.getCurrentItem(); mViewPager.setCurrentItem(i); if (mTabListener != null) { if (previousPos != i) { mTabListener.onTabSelected(i); } else { mTabListener.onTabReSelected(i); } } return; } } } } static class SlidingTabStrip extends LinearLayout { private int mSelectedIndicatorHeight; private final Paint mSelectedIndicatorPaint; private int mSelectedPosition; private float mSelectionOffset; SlidingTabStrip(Context context) { super(context); setWillNotDraw(false); mSelectedIndicatorPaint = new Paint(); } void setSelectedIndicatorColor(@ColorInt int color) { mSelectedIndicatorPaint.setColor(color); invalidate(); } void setSelectedIndicatorHeight(int height) { mSelectedIndicatorHeight = height; invalidate(); } void onViewPagerPageChanged(int position, float positionOffset) { mSelectedPosition = position; mSelectionOffset = positionOffset; invalidate(); } @Override protected void onDraw(Canvas canvas) { final int height = getHeight(); final int childCount = getChildCount(); // Thick colored underline below the current selection if (childCount > 0) { View selectedTitle = getChildAt(mSelectedPosition); int left = selectedTitle.getLeft(); int right = selectedTitle.getRight(); if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { // Draw the selection partway between the tabs View nextTitle = getChildAt(mSelectedPosition + 1); left = (int) (mSelectionOffset * nextTitle.getLeft() + (1.0f - mSelectionOffset) * left); right = (int) (mSelectionOffset * nextTitle.getRight() + (1.0f - mSelectionOffset) * right); } canvas.drawRect(left, height - mSelectedIndicatorHeight, right, height, mSelectedIndicatorPaint); } } } }