先上一张图看下效果:
" M: [4 {8 q$ M+ H. j9 d% m6 \
3 _$ O- O0 [- ~7 C1 F+ k7 B
主要实现功能有:5 c( S& K) f' }6 x' n
1.支持左右滑动,每次滑动一个tab
( D3 ^ A. u( ?9 y' y7 @1 t5 i. L/ y 2.支持tab点击,直接跳到对应tab
; e6 j p u) H- _; u/ J* H 3.选中的tab一直处于居中位置
' c/ u7 f9 u6 c6 G3 ~ 4.支持部分UI自定义(大家可根据需要自己改动) v" m1 \8 t+ a) c- _
5.tab点击回调5 c* ]* Q: G# b% g3 K# p9 V
6.内置Tab接口,放入的内容需要实现Tab接口8 l5 ]- D+ _) Y8 u% ^! [3 f, Q
7.设置预选中tab
y. i2 n2 F7 d3 @
- public class CameraIndicator extends LinearLayout {
- // 当前选中的位置索引
- private int currentIndex;
- //tabs集合
- private Tab[] tabs;
-
- // 利用Scroller类实现最终的滑动效果
- public Scroller mScroller;
- //滑动执行时间(ms)
- private int mDuration = 300;
- //选中text的颜色
- private int selectedTextColor = 0xffffffff;
- //未选中的text的颜色
- private int normalTextColor = 0xffffffff;
- //选中的text的背景
- private Drawable selectedTextBackgroundDrawable;
- private int selectedTextBackgroundColor;
- private int selectedTextBackgroundResources;
- //是否正在滑动
- private boolean isScrolling = false;
-
- private int onLayoutCount = 0;
-
-
- public CameraIndicator(Context context) {
- this(context, null);
- }
-
- public CameraIndicator(Context context, @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public CameraIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mScroller = new Scroller(context);
-
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int widthMode = MeasureSpec.getMode(widthMeasureSpec);
- int widthSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- //测量所有子元素
- measureChildren(widthMeasureSpec, heightMeasureSpec);
- //处理wrap_content的情况
- int width = 0;
- int height = 0;
- if (getChildCount() == 0) {
- setMeasuredDimension(0, 0);
- } else if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- width += child.getMeasuredWidth();
- height = Math.max(height, child.getMeasuredHeight());
- }
- setMeasuredDimension(width, height);
- } else if (widthMode == MeasureSpec.AT_MOST) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- width += child.getMeasuredWidth();
- }
- setMeasuredDimension(width, heightSize);
- } else if (heightMode == MeasureSpec.AT_MOST) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- height = Math.max(height, child.getMeasuredHeight());
- }
- setMeasuredDimension(widthSize, height);
- } else {
- //如果自定义ViewGroup之初就已确认该ViewGroup宽高都是match_parent,那么直接设置即可
- setMeasuredDimension(widthSize, heightSize);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- //给选中text的添加背景会多次进入onLayout,会导致位置有问题,暂未解决
- if (onLayoutCount > 0) {
- return;
- }
- onLayoutCount++;
-
- int counts = getChildCount();
- int childLeft = 0;
- int childRight = 0;
- int childTop = 0;
- int childBottom = 0;
- //居中显示
- int widthOffset = 0;
-
-
- //计算最左边的子view距离中心的距离
- for (int i = 0; i < currentIndex; i++) {
- View childView = getChildAt(i);
- widthOffset += childView.getMeasuredWidth() + getMargins(childView).get(0)+getMargins(childView).get(2);
- }
-
- //计算出每个子view的位置
- for (int i = 0; i < counts; i++) {
- View childView = getChildAt(i);
- childView.setOnClickListener(v -> moveTo(v));
- if (i != 0) {
- View preView = getChildAt(i - 1);
- childLeft = preView.getRight() +getMargins(preView).get(2)+ getMargins(childView).get(0);
- } else {
- childLeft = (getWidth() - getChildAt(currentIndex).getMeasuredWidth()) / 2 - widthOffset;
- }
- childRight = childLeft + childView.getMeasuredWidth();
- childTop = (getHeight() - childView.getMeasuredHeight()) / 2;
- childBottom = (getHeight() + childView.getMeasuredHeight()) / 2;
- childView.layout(childLeft, childTop, childRight, childBottom);
- }
-
- TextView indexText = (TextView) getChildAt(currentIndex);
- changeSelectedUIState(indexText);
-
|