zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    PHP使用引用变量foreach时,切记其他循环不要使用同一个名字的变量
    PHP 获取给定时间的周日时间或月末时间或每天
    MySQL Load Data InFile 文件内容导入数据库和 Into OutFile导出数据到文件
    直接拿来用!最火的iOS开源项目(一)
    12个有趣的C语言问答
    Flex,Flash,AS3,AIR的关系和区别
    Stage3D大冒险
    c/c++程序中内存区划分
    IOS—— strong weak retain assign 学习
    如何提高你的移动开发中AS3/AIR性能
  • 原文地址:https://www.cnblogs.com/shanzei/p/4654202.html
Copyright © 2011-2022 走看看