Loading res/color/sliding_tab_title_text_color.xml 0 → 100644 +22 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2016 Android Open Source Project 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. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="false" android:color="?android:attr/textColorSecondary"/> <item android:color="?android:attr/textColorPrimary"/> </selector> res/layout/sliding_tab_indicator_view.xml 0 → 100644 +23 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2016 The Android Open Source Project 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. --> <View xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/sliding_tab_selected_indicator" android:layout_width="wrap_content" android:layout_height="@dimen/pager_tabs_selected_indicator_height" android:background="?android:attr/colorAccent" android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" /> res/layout/sliding_tab_title_view.xml 0 → 100644 +31 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2016 Android Open Source Project 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. --> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:ellipsize="end" android:fontFamily="sans-serif-medium" android:gravity="center" android:maxLines="1" android:padding="@dimen/pager_tabs_title_padding" android:textColor="@color/sliding_tab_title_text_color" android:textAllCaps="true" android:theme="?android:attr/actionBarTheme"/> res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,8 @@ <dimen name="appwidget_min_height">40dip</dimen> <dimen name="pager_tabs_padding">0dp</dimen> <dimen name="pager_tabs_title_padding">16dp</dimen> <dimen name="pager_tabs_selected_indicator_height">3dp</dimen> <!-- Minimum width for the popup for updating a user's photo. --> <dimen name="update_user_photo_popup_min_width">300dip</dimen> Loading src/com/android/settings/widget/SlidingTabLayout.java 0 → 100644 +186 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * 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 com.android.settings.widget; import android.content.Context; import android.support.v4.view.PagerAdapter; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import com.android.settings.R; /** * To be used with ViewPager to provide a tab indicator component which give constant feedback as * to the user's scroll progress. */ public final class SlidingTabLayout extends FrameLayout implements View.OnClickListener { private final LinearLayout mTitleView; private final View mIndicatorView; private final LayoutInflater mLayoutInflater; private RtlCompatibleViewPager mViewPager; private int mSelectedPosition; private float mSelectionOffset; public SlidingTabLayout(Context context, AttributeSet attrs) { super(context, attrs); mLayoutInflater = LayoutInflater.from(context); mTitleView = new LinearLayout(context); mTitleView.setGravity(Gravity.CENTER_HORIZONTAL); mIndicatorView = mLayoutInflater.inflate(R.layout.sliding_tab_indicator_view, this, false); addView(mTitleView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); addView(mIndicatorView, mIndicatorView.getLayoutParams()); } /** * Sets the associated view pager. Note that the assumption here is that the pager content * (number of tabs and tab titles) does not change after this call has been made. */ public void setViewPager(RtlCompatibleViewPager viewPager) { mTitleView.removeAllViews(); mViewPager = viewPager; if (viewPager != null) { viewPager.addOnPageChangeListener(new InternalViewPagerListener()); populateTabStrip(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int titleCount = mTitleView.getChildCount(); if (titleCount > 0) { final int width = MeasureSpec.makeMeasureSpec( mTitleView.getMeasuredWidth() / titleCount, MeasureSpec.EXACTLY); final int height = MeasureSpec.makeMeasureSpec( mIndicatorView.getMeasuredHeight(), MeasureSpec.EXACTLY); mIndicatorView.measure(width, height); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (mTitleView.getChildCount() > 0) { final int indicatorBottom = getMeasuredHeight(); final int indicatorHeight = mIndicatorView.getMeasuredHeight(); final int indicatorWidth = mIndicatorView.getMeasuredWidth(); final int totalWidth = getMeasuredWidth(); final int leftPadding = getPaddingLeft(); final int rightPadding = getPaddingRight(); mTitleView.layout(leftPadding, 0, mTitleView.getMeasuredWidth() + rightPadding, mTitleView.getMeasuredHeight()); // IndicatorView should start on the right when RTL mode is enabled if (isRtlMode()) { mIndicatorView.layout(totalWidth - indicatorWidth, indicatorBottom - indicatorHeight, totalWidth, indicatorBottom); } else { mIndicatorView.layout(0, indicatorBottom - indicatorHeight, indicatorWidth, indicatorBottom); } } } @Override public void onClick(View v) { final int titleCount = mTitleView.getChildCount(); for (int i = 0; i < titleCount; i++) { if (v == mTitleView.getChildAt(i)) { mViewPager.setCurrentItem(i); return; } } } private void onViewPagerPageChanged(int position, float positionOffset) { mSelectedPosition = position; mSelectionOffset = positionOffset; // Translation should be reversed in RTL mode final int leftIndicator = isRtlMode() ? -getIndicatorLeft() : getIndicatorLeft(); mIndicatorView.setTranslationX(leftIndicator); } private void populateTabStrip() { final PagerAdapter adapter = mViewPager.getAdapter(); for (int i = 0; i < adapter.getCount(); i++) { final TextView tabTitleView = (TextView) mLayoutInflater.inflate( R.layout.sliding_tab_title_view, mTitleView, false); tabTitleView.setText(adapter.getPageTitle(i)); tabTitleView.setOnClickListener(this); mTitleView.addView(tabTitleView); tabTitleView.setSelected(i == mViewPager.getCurrentItem()); } } private int getIndicatorLeft() { View selectedTitle = mTitleView.getChildAt(mSelectedPosition); int left = selectedTitle.getLeft(); if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { View nextTitle = mTitleView.getChildAt(mSelectedPosition + 1); left = (int) (mSelectionOffset * nextTitle.getLeft() + (1.0f - mSelectionOffset) * left); } return left; } private boolean isRtlMode() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL; } private final class InternalViewPagerListener implements RtlCompatibleViewPager.OnPageChangeListener { private int mScrollState; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { final int titleCount = mTitleView.getChildCount(); if ((titleCount == 0) || (position < 0) || (position >= titleCount)) { return; } onViewPagerPageChanged(position, positionOffset); } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; } @Override public void onPageSelected(int position) { position = mViewPager.getRtlAwareIndex(position); if (mScrollState == RtlCompatibleViewPager.SCROLL_STATE_IDLE) { onViewPagerPageChanged(position, 0f); } final int titleCount = mTitleView.getChildCount(); for (int i = 0; i < titleCount; i++) { mTitleView.getChildAt(i).setSelected(position == i); } } } } Loading
res/color/sliding_tab_title_text_color.xml 0 → 100644 +22 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2016 Android Open Source Project 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. --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="false" android:color="?android:attr/textColorSecondary"/> <item android:color="?android:attr/textColorPrimary"/> </selector>
res/layout/sliding_tab_indicator_view.xml 0 → 100644 +23 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2016 The Android Open Source Project 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. --> <View xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/sliding_tab_selected_indicator" android:layout_width="wrap_content" android:layout_height="@dimen/pager_tabs_selected_indicator_height" android:background="?android:attr/colorAccent" android:theme="@*android:style/ThemeOverlay.DeviceDefault.Accent" />
res/layout/sliding_tab_title_view.xml 0 → 100644 +31 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2016 Android Open Source Project 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. --> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:background="?android:attr/selectableItemBackground" android:ellipsize="end" android:fontFamily="sans-serif-medium" android:gravity="center" android:maxLines="1" android:padding="@dimen/pager_tabs_title_padding" android:textColor="@color/sliding_tab_title_text_color" android:textAllCaps="true" android:theme="?android:attr/actionBarTheme"/>
res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,8 @@ <dimen name="appwidget_min_height">40dip</dimen> <dimen name="pager_tabs_padding">0dp</dimen> <dimen name="pager_tabs_title_padding">16dp</dimen> <dimen name="pager_tabs_selected_indicator_height">3dp</dimen> <!-- Minimum width for the popup for updating a user's photo. --> <dimen name="update_user_photo_popup_min_width">300dip</dimen> Loading
src/com/android/settings/widget/SlidingTabLayout.java 0 → 100644 +186 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * 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 com.android.settings.widget; import android.content.Context; import android.support.v4.view.PagerAdapter; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import com.android.settings.R; /** * To be used with ViewPager to provide a tab indicator component which give constant feedback as * to the user's scroll progress. */ public final class SlidingTabLayout extends FrameLayout implements View.OnClickListener { private final LinearLayout mTitleView; private final View mIndicatorView; private final LayoutInflater mLayoutInflater; private RtlCompatibleViewPager mViewPager; private int mSelectedPosition; private float mSelectionOffset; public SlidingTabLayout(Context context, AttributeSet attrs) { super(context, attrs); mLayoutInflater = LayoutInflater.from(context); mTitleView = new LinearLayout(context); mTitleView.setGravity(Gravity.CENTER_HORIZONTAL); mIndicatorView = mLayoutInflater.inflate(R.layout.sliding_tab_indicator_view, this, false); addView(mTitleView, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); addView(mIndicatorView, mIndicatorView.getLayoutParams()); } /** * Sets the associated view pager. Note that the assumption here is that the pager content * (number of tabs and tab titles) does not change after this call has been made. */ public void setViewPager(RtlCompatibleViewPager viewPager) { mTitleView.removeAllViews(); mViewPager = viewPager; if (viewPager != null) { viewPager.addOnPageChangeListener(new InternalViewPagerListener()); populateTabStrip(); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int titleCount = mTitleView.getChildCount(); if (titleCount > 0) { final int width = MeasureSpec.makeMeasureSpec( mTitleView.getMeasuredWidth() / titleCount, MeasureSpec.EXACTLY); final int height = MeasureSpec.makeMeasureSpec( mIndicatorView.getMeasuredHeight(), MeasureSpec.EXACTLY); mIndicatorView.measure(width, height); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (mTitleView.getChildCount() > 0) { final int indicatorBottom = getMeasuredHeight(); final int indicatorHeight = mIndicatorView.getMeasuredHeight(); final int indicatorWidth = mIndicatorView.getMeasuredWidth(); final int totalWidth = getMeasuredWidth(); final int leftPadding = getPaddingLeft(); final int rightPadding = getPaddingRight(); mTitleView.layout(leftPadding, 0, mTitleView.getMeasuredWidth() + rightPadding, mTitleView.getMeasuredHeight()); // IndicatorView should start on the right when RTL mode is enabled if (isRtlMode()) { mIndicatorView.layout(totalWidth - indicatorWidth, indicatorBottom - indicatorHeight, totalWidth, indicatorBottom); } else { mIndicatorView.layout(0, indicatorBottom - indicatorHeight, indicatorWidth, indicatorBottom); } } } @Override public void onClick(View v) { final int titleCount = mTitleView.getChildCount(); for (int i = 0; i < titleCount; i++) { if (v == mTitleView.getChildAt(i)) { mViewPager.setCurrentItem(i); return; } } } private void onViewPagerPageChanged(int position, float positionOffset) { mSelectedPosition = position; mSelectionOffset = positionOffset; // Translation should be reversed in RTL mode final int leftIndicator = isRtlMode() ? -getIndicatorLeft() : getIndicatorLeft(); mIndicatorView.setTranslationX(leftIndicator); } private void populateTabStrip() { final PagerAdapter adapter = mViewPager.getAdapter(); for (int i = 0; i < adapter.getCount(); i++) { final TextView tabTitleView = (TextView) mLayoutInflater.inflate( R.layout.sliding_tab_title_view, mTitleView, false); tabTitleView.setText(adapter.getPageTitle(i)); tabTitleView.setOnClickListener(this); mTitleView.addView(tabTitleView); tabTitleView.setSelected(i == mViewPager.getCurrentItem()); } } private int getIndicatorLeft() { View selectedTitle = mTitleView.getChildAt(mSelectedPosition); int left = selectedTitle.getLeft(); if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { View nextTitle = mTitleView.getChildAt(mSelectedPosition + 1); left = (int) (mSelectionOffset * nextTitle.getLeft() + (1.0f - mSelectionOffset) * left); } return left; } private boolean isRtlMode() { return getLayoutDirection() == LAYOUT_DIRECTION_RTL; } private final class InternalViewPagerListener implements RtlCompatibleViewPager.OnPageChangeListener { private int mScrollState; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { final int titleCount = mTitleView.getChildCount(); if ((titleCount == 0) || (position < 0) || (position >= titleCount)) { return; } onViewPagerPageChanged(position, positionOffset); } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; } @Override public void onPageSelected(int position) { position = mViewPager.getRtlAwareIndex(position); if (mScrollState == RtlCompatibleViewPager.SCROLL_STATE_IDLE) { onViewPagerPageChanged(position, 0f); } final int titleCount = mTitleView.getChildCount(); for (int i = 0; i < titleCount; i++) { mTitleView.getChildAt(i).setSelected(position == i); } } } }