• Android淘宝电影日期选项卡的实现-tab 栏居中滚动


    在淘宝电影上面有这样一个功能,日期可以滑动,并且选中的是在正中间,效果如下:

    20150611213914791.gif

    看完了,那么问题来了。这个功能怎么实现呢?

    我们先来分析一下: 
    把功能拆分一下来看,如果不能滚动,是不是很好实现?其实就是一个 tab 栏,我在前面的 blog 中Android 快速实现 ViewPager 滑动页卡切换(可用作整个 app上导航) 中就实现了此功能,然后在此功能的基础上加上滚动功能即可,具体的实现原理是通过水平滚动控件 HorizontalScrollView把 tab 栏包含起来,然后通过  tab 的选中item 来控制HorizontalScrollView的滚动。

    代码实现:

    1、实现 自定义 tab,这里就不细讲了,跟前面那篇 blog 几乎一样,直接贴代码了,不清楚的请看前面的 blog

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    package toolbar.scrollstripview;
     
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.util.TypedValue;
    import android.view.View;
    import android.widget.LinearLayout;
    import android.widget.TextView;
     
    /**
     * Created by moon.zhong on 2015/5/25.
     */
    public class SlidingTabView extends LinearLayout {
     
        private static final float DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0.5f;
        private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
        private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;
        private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
     
        private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
        private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
        private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
     
        private final float mBottomBorderThickness;
        private final Paint mBottomBorderPaint;
     
        private final int mSelectedIndicatorThickness;
        private final Paint mSelectedIndicatorPaint;
     
        private final Paint mDividerPaint;
        private final float mDividerHeight;
     
        private int mSelectedPosition;
        private float mSelectionOffset;
     
        public SlidingTabView(Context context) {
            this(context, null);
        }
     
        public SlidingTabView(Context context, AttributeSet attrs) {
            super(context, attrs);
            setWillNotDraw(false);
            final float density = getResources().getDisplayMetrics().density;
     
            TypedValue outValue = new TypedValue();
            getContext().getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
            final int themeForegroundColor =  outValue.data;
     
            mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
            mBottomBorderPaint = new Paint();
            mBottomBorderPaint.setColor(getResources().getColor(R.color.color_line));
     
            mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
            mSelectedIndicatorPaint = new Paint();
            mSelectedIndicatorPaint.setColor(0xffff1322);
     
            mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
            mDividerPaint = new Paint();
            mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
            mDividerPaint.setColor(getResources().getColor(R.color.color_line));
        }
     
        void viewPagerChange(int position, float offset){
            this.mSelectedPosition = position ;
            this.mSelectionOffset = offset ;
            if (offset == 0){
                for (int i = 0; i < getChildCount(); i++) {
                    TextView child = (TextView) getChildAt(i);
                    child.setTextColor(0xff666666);
                }
                TextView selectedTitle = (TextView) getChildAt(mSelectedPosition);
                selectedTitle.setTextColor(0xffff1322);
            }
            invalidate();
        }
     
        @Override
        protected void dispatchDraw(Canvas canvas) {
            super.dispatchDraw(canvas);
            final int height = getHeight();
            final int childCount = getChildCount();
            final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
     
            if (childCount > 0) {
                TextView selectedTitle = (TextView) getChildAt(mSelectedPosition);
                int left = selectedTitle.getLeft();
                int right = selectedTitle.getRight();
                selectedTitle.setTextColor(blendColors(0xff666666,0xffff1322,mSelectionOffset));
     
                if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
     
                    TextView nextTitle = (TextView) getChildAt(mSelectedPosition + 1);
                    left = (int) (mSelectionOffset * nextTitle.getLeft() +
                            (1.0f - mSelectionOffset) * left);
                    right = (int) (mSelectionOffset * nextTitle.getRight() +
                            (1.0f - mSelectionOffset) * right);
                    nextTitle.setTextColor(blendColors(0xffff1322,0xff666666,mSelectionOffset));
                }
     
                canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
                        height, mSelectedIndicatorPaint);
            }
     
            canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
     
            int separatorTop = (height - dividerHeightPx) / 2;
            for (int i = 0; i < childCount - 1; i++) {
                View child = getChildAt(i);
     
                canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
                        separatorTop + dividerHeightPx, mDividerPaint);
            }
        }
        private static int blendColors(int color1, int color2, float ratio) {
            final float inverseRation = 1f - ratio;
            float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
            float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
            float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
            return Color.rgb((int) r, (int) g, (int) b);
        }
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
        }
     
    }

    2、自定义HorizontalScrollView 
    这个自定义类的功能,主要是填充 tab 的数据,通过选中的 item 来滚动HorizontalScrollView 
    填充数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
        private void populateTabStrip() {
            final PagerAdapter adapter = mViewPager.getAdapter();
            final OnClickListener tabClickListener = new TabClickListener();
    /**/
            /*通过 viewPager 的 item 来确定tab 的个数*/
     
            for (int i = 0; i < adapter.getCount(); i++) {
                View tabView = null;
                TextView tabTitleView = null;
     
                if (mTabViewLayoutId != 0) {
                    // If there is a custom tab view layout id set, try and inflate it
                    tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
                            false);
                    tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
                }
     
                if (tabView == null) {
                    /*创建textView*/
                    tabView = createDefaultTabView(getContext());
                }
     
                if (tabTitleView == null && TextView.class.isInstance(tabView)) {
                    tabTitleView = (TextView) tabView;
                }
     
                tabTitleView.setText(adapter.getPageTitle(i));
                tabView.setOnClickListener(tabClickListener);
                tabView.setBackgroundResource(R.drawable.item_selector_bg);
                /*把 textView 放入到自定义的 tab 栏中*/
                mTabStrip.addView(tabView);
            }
        }

    滚动 ScrollView

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
        /**
         * 这个方法是关键
         * 滚动 scrollview
         * @param tabIndex
         * @param positionOffset
         */
        private void scrollToTab(int tabIndex, int positionOffset) {
            final int tabStripChildCount = mTabStrip.getChildCount();
            if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
                return;
            }
     
            /*获取当前选中的 item*/
            View selectedChild = mTabStrip.getChildAt(tabIndex);
            if (selectedChild != null) {
                /*获取当前 item 的偏移量*/
                int targetScrollX = selectedChild.getLeft() + positionOffset;
                /*item 的宽度*/
                int width = selectedChild.getWidth();
                /*item 距离正中间的偏移量*/
                mTitleOffset = (int) ((mWidth-width)/2.0f);
     
                if (tabIndex > 0 || positionOffset > 0) {
                    /*计算出正在的偏移量*/
                    targetScrollX -= mTitleOffset;
                }
                Log.v("zgy","==================mWidth======="+mWidth) ;
                /*这个时候偏移的量就是屏幕的正中间*/
                scrollTo(targetScrollX, 0);
            }
        }

    具体的调用当然就是 在 ViewPager 的OnPageChangeListener中。 
    完整类的代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    package toolbar.scrollstripview;
     
    import android.app.Activity;
    import android.content.Context;
    import android.os.Build;
    import android.support.v4.view.PagerAdapter;
    import android.support.v4.view.ViewPager;
    import android.util.AttributeSet;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.util.TypedValue;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.HorizontalScrollView;
    import android.widget.TextView;
     
    /**
     * Created by moon.zhong.
     */
    public class SlidingTabLayout extends HorizontalScrollView {
        private static final int TITLE_OFFSET_DIPS = 24;
        private static final int TAB_VIEW_PADDING_DIPS_TB = 15;
        private static final int TAB_VIEW_PADDING_DIPS = 16;
        private static final int TAB_VIEW_TEXT_SIZE_SP = 14;
        private int mTitleOffset;
     
        private int mTabViewLayoutId;
        private int mTabViewTextViewId;
     
        private ViewPager mViewPager;
        private final SlidingTabView mTabStrip;
     
        private int mWidth ;
        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);
     
            setHorizontalScrollBarEnabled(false);
     
            setFillViewport(true);
     
            mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
     
            mTabStrip = new SlidingTabView(context);
            addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
            DisplayMetrics displayMetrics = new DisplayMetrics() ;
            ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
            mWidth = (int) (displayMetrics.widthPixels) ;
        }
        public void setViewPager(ViewPager viewPager) {
            mTabStrip.removeAllViews();
     
            mViewPager = viewPager;
            if (viewPager != null) {
                viewPager.addOnPageChangeListener(new InternalViewPagerListener());
                populateTabStrip();
            }
        }
        private void populateTabStrip() {
            final PagerAdapter adapter = mViewPager.getAdapter();
            final OnClickListener tabClickListener = new TabClickListener();
    /**/
            /*通过 viewPager 的 item 来确定tab 的个数*/
     
            for (int i = 0; i < adapter.getCount(); i++) {
                View tabView = null;
                TextView tabTitleView = null;
     
                if (mTabViewLayoutId != 0) {
                    // If there is a custom tab view layout id set, try and inflate it
                    tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
                            false);
                    tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
                }
     
                if (tabView == null) {
                    /*创建textView*/
                    tabView = createDefaultTabView(getContext());
                }
     
                if (tabTitleView == null && TextView.class.isInstance(tabView)) {
                    tabTitleView = (TextView) tabView;
                }
     
                tabTitleView.setText(adapter.getPageTitle(i));
                tabView.setOnClickListener(tabClickListener);
                tabView.setBackgroundResource(R.drawable.item_selector_bg);
                /*把 textView 放入到自定义的 tab 栏中*/
                mTabStrip.addView(tabView);
            }
        }
        /*这里就是创建 textView,没什么可讲的*/
        protected TextView createDefaultTabView(Context context) {
            TextView textView = new TextView(context);
            textView.setGravity(Gravity.CENTER);
            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);;
     
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
     
                TypedValue outValue = new TypedValue();
                getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
                        outValue, true);
                textView.setBackgroundResource(outValue.resourceId);
            }
     
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                textView.setAllCaps(true);
            }
     
            int paddingTB = (int) (TAB_VIEW_PADDING_DIPS_TB * getResources().getDisplayMetrics().density);
            textView.setPadding(0, paddingTB, 0, paddingTB);
            textView.setTextColor(0xff666666);
            int width = (int) (100 * getResources().getDisplayMetrics().density);
            ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT) ;
            textView.setLayoutParams(params);
            return textView;
        }
     
        /**
         * 这个方法是关键
         * 滚动 scrollview
         * @param tabIndex
         * @param positionOffset
         */
        private void scrollToTab(int tabIndex, int positionOffset) {
            final int tabStripChildCount = mTabStrip.getChildCount();
            if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
                return;
            }
     
            /*获取当前选中的 item*/
            View selectedChild = mTabStrip.getChildAt(tabIndex);
            if (selectedChild != null) {
                /*获取当前 item 的偏移量*/
                int targetScrollX = selectedChild.getLeft() + positionOffset;
                /*item 的宽度*/
                int width = selectedChild.getWidth();
                /*item 距离正中间的偏移量*/
                mTitleOffset = (int) ((mWidth-width)/2.0f);
     
                if (tabIndex > 0 || positionOffset > 0) {
                    /*计算出正在的偏移量*/
                    targetScrollX -= mTitleOffset;
                }
                Log.v("zgy","==================mWidth======="+mWidth) ;
                /*这个时候偏移的量就是屏幕的正中间*/
                scrollTo(targetScrollX, 0);
            }
        }
     
     
        private 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.viewPagerChange(position, positionOffset);
     
                View selectedTitle = mTabStrip.getChildAt(position);
                int extraOffset = (selectedTitle != null)
                        ? (int) (positionOffset * selectedTitle.getWidth())
                        : 0;
                scrollToTab(position, extraOffset);
     
     
            }
     
            @Override
            public void onPageScrollStateChanged(int state) {
                mScrollState = state;
     
            }
     
            @Override
            public void onPageSelected(int position) {
                if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
                    mTabStrip.viewPagerChange(position, 0f);
                    scrollToTab(position, 0);
                }
     
            }
     
        }
        private class TabClickListener implements OnClickListener {
            @Override
            public void onClick(View v) {
                for (int i = 0; i < mTabStrip.getChildCount(); i++) {
                    if (v == mTabStrip.getChildAt(i)) {
                        mViewPager.setCurrentItem(i);
                        return;
                    }
                }
            }
        }
    }

    代码引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
     
        <toolbar.scrollstripview.SlidingTabLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/id_sliding_view"/>
        <android.support.v4.view.ViewPager
            android:id="@+id/id_view_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/id_sliding_view"/>
    </RelativeLayout>

    运行效果:

    20150611221023785.gif

    总结: 
    1、对前面Android 快速实现 ViewPager 滑动页卡切换(可用作整个 app上导航) blog 的运用; 
    2、scrollTo(targetScrollX, 0);方法的灵活运用,targetScrollX如果小于0,获取targetScrollX大于 view 的宽度的时候这个方法都不会起作用,看源码可知:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        public void scrollTo(int x, int y) {
            // we rely on the fact the View.scrollBy calls scrollTo.
            if (getChildCount() > 0) {
                View child = getChildAt(0);
                x = clamp(x, getWidth() - mPaddingRight - mPaddingLeft, child.getWidth());
                y = clamp(y, getHeight() - mPaddingBottom - mPaddingTop, child.getHeight());
                if (x != mScrollX || y != mScrollY) {
                    super.scrollTo(x, y);
                }
            }
        }

    对 x、y 做了相应的截取操作

    源码下载

    http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0615/3045.html

  • 相关阅读:
    开源项目中标准文件命名和实践
    linux远程拷贝命令-scp
    Linux访问Windows共享目录的方法——smbclient
    ADB Fix error : insufficient permissions for device
    APT典型应用示例
    我的参考书籍列表
    GCC Reference
    GNU make简介
    Windows下搭建Android NDK开发环境及命令行编译
    Git命令行基本操作
  • 原文地址:https://www.cnblogs.com/shanzei/p/4654202.html
Copyright © 2020-2023  润新知