Set the current page of both the ViewPager and indicator.
45 | * 46 | *This must be used if you need to set the page before 47 | * the views are drawn on screen (e.g., default start page).
48 | * 49 | * @param item 50 | */ 51 | void setCurrentItem(int item); 52 | 53 | /** 54 | * Set a page change listener which will receive forwarded events. 55 | * 56 | * @param listener 57 | */ 58 | void setOnPageChangeListener(ViewPager.OnPageChangeListener listener); 59 | 60 | /** 61 | * Notify the indicator that the fragment list has changed. 62 | */ 63 | void notifyDataSetChanged(); 64 | } 65 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/TabPageIndicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * Copyright (C) 2011 Jake Wharton 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package com.jiang.android.indicator; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.Context; 21 | import android.graphics.Canvas; 22 | import android.graphics.Paint; 23 | import android.graphics.drawable.ShapeDrawable; 24 | import android.graphics.drawable.shapes.OvalShape; 25 | import android.os.Build; 26 | import android.support.v4.view.PagerAdapter; 27 | import android.support.v4.view.ViewPager; 28 | import android.util.AttributeSet; 29 | import android.view.View; 30 | import android.view.ViewGroup; 31 | import android.widget.HorizontalScrollView; 32 | import android.widget.LinearLayout; 33 | import android.widget.TextView; 34 | 35 | import com.jiang.android.indicator.adapter.IconPagerAdapter; 36 | import com.jiang.android.indicator.adapter.ModelFragmentPagerAdapter; 37 | import com.jiang.android.indicator.model.PageModel; 38 | 39 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 40 | import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 41 | 42 | /** 43 | * This widget implements the dynamic action bar tab behavior that can change 44 | * across different configurations or circumstances. 45 | */ 46 | public class TabPageIndicator extends HorizontalScrollView implements PageIndicator { 47 | /** 48 | * Title text used when no title is provided by the adapter. 49 | */ 50 | private static final CharSequence EMPTY_TITLE = ""; 51 | private int oldSelected; 52 | private int mAllWidth; 53 | private int mAllHeight; 54 | 55 | /** 56 | * Interface for a callback when the selected tab has been reselected. 57 | */ 58 | public interface OnTabReselectedListener { 59 | /** 60 | * Callback when the selected tab has been reselected. 61 | * 62 | * @param position Position of the current center item. 63 | */ 64 | void onTabReselected(int position); 65 | } 66 | 67 | private Runnable mTabSelector; 68 | 69 | private final OnClickListener mTabClickListener = new OnClickListener() { 70 | public void onClick(View view) { 71 | TabView tabView = (TabView) view; 72 | oldSelected = mViewPager.getCurrentItem(); 73 | final int newSelected = tabView.getIndex(); 74 | mViewPager.setCurrentItem(newSelected); 75 | if (oldSelected == newSelected && mTabReselectedListener != null) { 76 | mTabReselectedListener.onTabReselected(newSelected); 77 | } 78 | } 79 | }; 80 | 81 | private final IcsLinearLayout mTabLayout; 82 | 83 | private ViewPager mViewPager; 84 | private ViewPager.OnPageChangeListener mListener; 85 | 86 | private int mMaxTabWidth; 87 | private int mSelectedTabIndex; 88 | 89 | private OnTabReselectedListener mTabReselectedListener; 90 | 91 | 92 | public TabPageIndicator(Context context) { 93 | this(context, null); 94 | } 95 | 96 | public TabPageIndicator(Context context, AttributeSet attrs) { 97 | super(context, attrs); 98 | setHorizontalScrollBarEnabled(false); 99 | 100 | mTabLayout = new IcsLinearLayout(context, R.attr.vpiTabPageIndicatorStyle); 101 | addView(mTabLayout, new ViewGroup.LayoutParams(WRAP_CONTENT, MATCH_PARENT)); 102 | 103 | } 104 | 105 | public void setOnTabReselectedListener(OnTabReselectedListener listener) { 106 | mTabReselectedListener = listener; 107 | } 108 | 109 | @Override 110 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 111 | final int widthMode = MeasureSpec.getMode(widthMeasureSpec); 112 | final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY; 113 | setFillViewport(lockedExpanded); 114 | 115 | final int childCount = mTabLayout.getChildCount(); 116 | if (childCount > 1 && (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) { 117 | if (childCount > 2) { 118 | mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) / 3); 119 | } else { 120 | mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2; 121 | } 122 | } else { 123 | mMaxTabWidth = -1; 124 | } 125 | 126 | final int oldWidth = getMeasuredWidth(); 127 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 128 | final int newWidth = getMeasuredWidth(); 129 | 130 | if (lockedExpanded && oldWidth != newWidth) { 131 | // Recenter the tab display if we're at a new (scrollable) size. 132 | setCurrentItem(mSelectedTabIndex); 133 | } 134 | 135 | mAllWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight(); 136 | mAllHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom(); 137 | } 138 | 139 | private void animateToTab(final int position) { 140 | TabView oldView = (TabView) mTabLayout.getChildAt(oldSelected); 141 | oldView.setTextSize(getNormalSize()); 142 | oldView.setTextColor(getContext().getResources().getColor(R.color.normal_item_textcolor)); 143 | final TabView tabView = (TabView) mTabLayout.getChildAt(position); 144 | tabView.setTextColor(getContext().getResources().getColor(R.color.selected_item_textcolor)); 145 | tabView.setTextSize(getSelectedSize()); 146 | 147 | 148 | if (mTabSelector != null) { 149 | removeCallbacks(mTabSelector); 150 | } 151 | mTabSelector = new Runnable() { 152 | public void run() { 153 | final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2; 154 | smoothScrollTo(scrollPos, 0); 155 | mTabSelector = null; 156 | } 157 | }; 158 | post(mTabSelector); 159 | } 160 | 161 | private float getSelectedSize() { 162 | float size = getContext().getResources().getDimension(R.dimen.selected_item_textsize); 163 | return px2sp(getContext(), size); 164 | } 165 | 166 | private float getNormalSize() { 167 | float size = getContext().getResources().getDimension(R.dimen.normal_item_textsize); 168 | return px2sp(getContext(), size); 169 | } 170 | 171 | public static int px2sp(Context context, float pxValue) { 172 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; 173 | return (int) (pxValue / fontScale + 0.5f); 174 | } 175 | 176 | @Override 177 | public void onAttachedToWindow() { 178 | super.onAttachedToWindow(); 179 | if (mTabSelector != null) { 180 | // Re-post the selector we saved 181 | post(mTabSelector); 182 | } 183 | } 184 | 185 | @Override 186 | public void onDetachedFromWindow() { 187 | super.onDetachedFromWindow(); 188 | if (mTabSelector != null) { 189 | removeCallbacks(mTabSelector); 190 | } 191 | } 192 | 193 | private void addTab(int index, CharSequence text, int count, int iconResId) { 194 | final TabView tabView = new TabView(getContext()); 195 | tabView.mIndex = index; 196 | tabView.setFocusable(true); 197 | tabView.setOnClickListener(mTabClickListener); 198 | tabView.setText(text); 199 | if (count > 0) { 200 | tabView.setNeedPoint(true); 201 | } else { 202 | tabView.setNeedPoint(false); 203 | } 204 | if (iconResId != 0) { 205 | tabView.setCompoundDrawablesWithIntrinsicBounds(iconResId, 0, 0, 0); 206 | } 207 | 208 | // mTabLayout.addView(tabView); 209 | mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0, MATCH_PARENT, 1)); 210 | } 211 | 212 | @Override 213 | public void onPageScrollStateChanged(int arg0) { 214 | 215 | if (arg0 == ViewPager.SCROLL_STATE_DRAGGING) { 216 | oldSelected = mViewPager.getCurrentItem(); 217 | } 218 | 219 | if (mListener != null) { 220 | mListener.onPageScrollStateChanged(arg0); 221 | } 222 | } 223 | 224 | @Override 225 | public void onPageScrolled(int arg0, float arg1, int arg2) { 226 | if (mListener != null) { 227 | mListener.onPageScrolled(arg0, arg1, arg2); 228 | } 229 | } 230 | 231 | @Override 232 | public void onPageSelected(int arg0) { 233 | setCurrentItem(arg0); 234 | if (mListener != null) { 235 | mListener.onPageSelected(arg0); 236 | } 237 | } 238 | 239 | @Override 240 | public void setViewPager(ViewPager view) { 241 | if (mViewPager == view) { 242 | return; 243 | } 244 | if (mViewPager != null) { 245 | mViewPager.setOnPageChangeListener(null); 246 | } 247 | final PagerAdapter adapter = view.getAdapter(); 248 | if (adapter == null) { 249 | throw new IllegalStateException("ViewPager does not have adapter instance."); 250 | } 251 | mViewPager = view; 252 | view.setOnPageChangeListener(this); 253 | notifyDataSetChanged(); 254 | } 255 | 256 | @Override 257 | protected void dispatchDraw(Canvas canvas) { 258 | super.dispatchDraw(canvas); 259 | } 260 | 261 | public void notifyDataSetChanged() { 262 | mTabLayout.removeAllViews(); 263 | ModelFragmentPagerAdapter adapter = (ModelFragmentPagerAdapter) mViewPager.getAdapter(); 264 | IconPagerAdapter iconAdapter = null; 265 | if (adapter instanceof IconPagerAdapter) { 266 | iconAdapter = (IconPagerAdapter) adapter; 267 | } 268 | final int count = adapter.getCount(); 269 | for (int i = 0; i < count; i++) { 270 | PageModel title = adapter.getPageModel(i); 271 | if (title == null) { 272 | title.setName(EMPTY_TITLE); 273 | } 274 | int iconResId = 0; 275 | if (iconAdapter != null) { 276 | iconResId = iconAdapter.getIconResId(i); 277 | } 278 | addTab(i, title.getName(), title.getCount(), iconResId); 279 | } 280 | if (mSelectedTabIndex > count) { 281 | mSelectedTabIndex = count - 1; 282 | } 283 | setCurrentItem(mSelectedTabIndex); 284 | requestLayout(); 285 | } 286 | 287 | @Override 288 | public void setViewPager(ViewPager view, int initialPosition) { 289 | setViewPager(view); 290 | setCurrentItem(initialPosition); 291 | } 292 | 293 | @Override 294 | public void setCurrentItem(int item) { 295 | if (mViewPager == null) { 296 | throw new IllegalStateException("ViewPager has not been bound."); 297 | } 298 | mSelectedTabIndex = item; 299 | 300 | mViewPager.setCurrentItem(item); 301 | 302 | final int tabCount = mTabLayout.getChildCount(); 303 | for (int i = 0; i < tabCount; i++) { 304 | final View child = mTabLayout.getChildAt(i); 305 | final boolean isSelected = (i == item); 306 | child.setSelected(isSelected); 307 | if (isSelected) { 308 | animateToTab(item); 309 | } 310 | } 311 | } 312 | 313 | @Override 314 | public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { 315 | mListener = listener; 316 | } 317 | 318 | private class TabView extends TextView { 319 | private int mIndex; 320 | private Paint paint; 321 | private boolean needPoint; 322 | private int RADIUS; 323 | private int PADDING; 324 | private final ShapeDrawable drawable; 325 | 326 | public boolean isNeedPoint() { 327 | return needPoint; 328 | } 329 | 330 | public void setNeedPoint(boolean needPoint) { 331 | this.needPoint = needPoint; 332 | invalidate(); 333 | } 334 | 335 | public TabView(Context context) { 336 | super(context, null, R.attr.vpiTabPageIndicatorStyle); 337 | paint = new Paint(); 338 | paint.setStyle(Paint.Style.FILL); 339 | paint.setColor(getContext().getResources().getColor(R.color.selected_item_textcolor)); 340 | RADIUS = dip2px(context, 4); 341 | PADDING = dip2px(context, 13); 342 | drawable = new ShapeDrawable(new OvalShape()); 343 | } 344 | 345 | @Override 346 | public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 347 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 348 | 349 | // Re-measure if we went beyond our maximum size. 350 | if (mMaxTabWidth > 0 && getMeasuredWidth() <= mMaxTabWidth) { 351 | super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY), 352 | heightMeasureSpec); 353 | } 354 | } 355 | 356 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) 357 | public void callOnClick(int item) { 358 | if (mTabLayout.getChildCount() > item) { 359 | mTabLayout.getChildAt(item).callOnClick(); 360 | } 361 | 362 | } 363 | 364 | public int getIndex() { 365 | return mIndex; 366 | } 367 | 368 | 369 | @Override 370 | protected void onDraw(Canvas canvas) { 371 | super.onDraw(canvas); 372 | int x = (int) (getWidth() * (float) (4 * 1.0 / 5)); 373 | int y = PADDING; 374 | if (needPoint) { 375 | //canvas.drawCircle(x, y, RADIUS, paint); 376 | drawable.getPaint().setColor(getResources().getColor(R.color.selected_item_textcolor)); 377 | drawable.setBounds(x, y, x + RADIUS * 2, y + RADIUS * 2); 378 | drawable.draw(canvas); 379 | } 380 | } 381 | 382 | public int dip2px(Context context, float dipValue) { 383 | final float scale = context.getResources().getDisplayMetrics().density; 384 | return (int) (dipValue * scale + 0.5f); 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/adapter/IconPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.jiang.android.indicator.adapter; 2 | 3 | public interface IconPagerAdapter { 4 | /** 5 | * Get icon representing the page at {@code index} in the adapter. 6 | */ 7 | int getIconResId(int index); 8 | 9 | // From PagerAdapter 10 | int getCount(); 11 | } 12 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/adapter/ModelFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * created by jiang, 16/2/26 3 | * Copyright (c) 2016, jyuesong@gmail.com All Rights Reserved. 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | */ 28 | 29 | package com.jiang.android.indicator.adapter; 30 | 31 | 32 | import android.support.v4.app.FragmentManager; 33 | import android.support.v4.app.FragmentPagerAdapter; 34 | 35 | import com.jiang.android.indicator.model.PageModel; 36 | 37 | 38 | /** 39 | * Created by jiang on 16/2/26. 40 | */ 41 | public abstract class ModelFragmentPagerAdapter extends FragmentPagerAdapter { 42 | 43 | 44 | public ModelFragmentPagerAdapter(FragmentManager fm) { 45 | super(fm); 46 | } 47 | 48 | 49 | @Override 50 | public CharSequence getPageTitle(int position) { 51 | getPageModel(position); 52 | return super.getPageTitle(position); 53 | } 54 | 55 | public abstract PageModel getPageModel(int position); 56 | } 57 | -------------------------------------------------------------------------------- /indicator/src/main/java/com/jiang/android/indicator/model/PageModel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * created by jiang, 16/2/26 3 | * Copyright (c) 2016, jyuesong@gmail.com All Rights Reserved. 4 | * * # # 5 | * # _oo0oo_ # 6 | * # o8888888o # 7 | * # 88" . "88 # 8 | * # (| -_- |) # 9 | * # 0\ = /0 # 10 | * # ___/`---'\___ # 11 | * # .' \\| |# '. # 12 | * # / \\||| : |||# \ # 13 | * # / _||||| -:- |||||- \ # 14 | * # | | \\\ - #/ | | # 15 | * # | \_| ''\---/'' |_/ | # 16 | * # \ .-\__ '-' ___/-. / # 17 | * # ___'. .' /--.--\ `. .'___ # 18 | * # ."" '< `.___\_<|>_/___.' >' "". # 19 | * # | | : `- \`.;`\ _ /`;.`/ - ` : | | # 20 | * # \ \ `_. \_ __\ /__ _/ .-` / / # 21 | * # =====`-.____`.___ \_____/___.-`___.-'===== # 22 | * # `=---=' # 23 | * # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | * # # 25 | * # 佛祖保佑 永无BUG # 26 | * # # 27 | */ 28 | 29 | package com.jiang.android.indicator.model; 30 | 31 | import java.io.Serializable; 32 | 33 | /** 34 | * Created by jiang on 16/2/26. 35 | */ 36 | public class PageModel implements Serializable { 37 | private CharSequence name; 38 | private int count; 39 | 40 | 41 | public PageModel() { 42 | } 43 | 44 | 45 | 46 | public PageModel(String name, int count) { 47 | this.name = name; 48 | this.count = count; 49 | } 50 | 51 | public CharSequence getName() { 52 | return name; 53 | } 54 | 55 | public void setName(CharSequence name) { 56 | this.name = name; 57 | } 58 | 59 | public int getCount() { 60 | return count; 61 | } 62 | 63 | public void setCount(int count) { 64 | this.count = count; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /indicator/src/main/res/color/vpi__dark_theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 |