模仿微博ViewPage指示器滑动效果的自定义View

来源:互联网 发布:环形网络拓扑结构图 编辑:程序博客网 时间:2024/06/05 15:17

github地址:https://github.com/shallcheek/CaterpillarIndicator

效果图

偶然看到在微博的最新版本看到发现界面和个人界面的几个Tab滑动的指示器添加了动画,看起来比以前的线条滑动看起来生动些。

实现

实现滑动是我们Android开发经常运用到的ViewPage 线条的滑动则是根据ViewPage 的Select Item 标识决定的。而相应的滑动动画效果则是根据ViewPage的监听事件OnPageChangeListener 中对应的回调方法

public void onPageScrolled(int position,float positionOffset,int positionOffsetPixels)

positionOffsetPixels是滑动距离,和屏幕有关,*手指往左滑动,该值递增,反之递减 对应的可以判断左右滑动然后根据值对线条做相应的处理

滑动距离计算公式

mCurrentScroll=控件的宽度*当前的position+positionOffsetPixels*(控件的宽度/ViewPage宽度)scroll_x = (mCurrentScroll- ((选择的ITEM) * (Indicator的宽度))) /总的ITEM数量

实现代码

@Overridepublic voidonPageScrolled(intposition, floatpositionOffset, intpositionOffsetPixels) {floata = (float) getWidth() / (float)mViewPager.getWidth();onScrolled((int) ((getWidth() +mViewPager.getPageMargin()) * position + positionOffsetPixels * a));}

计算出的的scroll_x则是线条的左右滑动距离 线条往左边移动是负数往右边是正数
所以线条长短变化实现代码如下:

 //获取单个item线条的宽度 不能超过View的宽度平分的宽度  this.mItemLineWidth = mItemLineWidth > cursorWidth ? cursorWidth : mItemLineWidth;  int mItemLeft;  int mItemRight;     if (mItemLineWidth < cursorWidth) {            mItemLeft = (cursorWidth - mItemLineWidth) / 2;            mItemRight = cursorWidth - mItemLeft;        } else {            mItemLeft = 0;            mItemRight = cursorWidth;        }            boolean isHalf = Math.abs(scroll_x) < (cursorWidth / 2);            if (scroll_x < 0) {                if (isHalf) {                    leftX = (mSelectedTab) * cursorWidth + scroll_x * 2 + mItemLeft;                    rightX = (mSelectedTab) * cursorWidth + mItemRight;                } else {                    leftX = (mSelectedTab - 1) * cursorWidth + mItemLeft;                    rightX = (mSelectedTab) * cursorWidth + mItemRight + (scroll_x + (cursorWidth / 2)) * 2;                }            } else if (scroll_x > 0) {                if (isHalf) {                    leftX = mSelectedTab * cursorWidth + mItemLeft;                    rightX = (mSelectedTab) * cursorWidth + mItemRight + scroll_x * 2;                } else {                    leftX = mSelectedTab * cursorWidth + mItemLeft + (scroll_x - (cursorWidth / 2)) * 2;                    rightX = (mSelectedTab + 1) * cursorWidth + mItemRight;                }            } else {                leftX = mSelectedTab * cursorWidth + mItemLeft;                rightX = mSelectedTab * cursorWidth + mItemRight;            }        }

接下来便就是根据线条的最左边和最右边距离的点便可以在View上面绘制出线条

        float bottomY = getHeight();        float topY = bottomY - mFooterLineHeight;        drawLineRect.left = leftX;        drawLineRect.right = rightX;        drawLineRect.bottom = bottomY;        drawLineRect.top = topY;        int roundXY = isRoundRectangleLine ? (mFooterLineHeight / 2) : 0;        canvas.drawRoundRect(drawLineRect, roundXY, roundXY, mPaintFooterLine);

完整代码

public class CaterpillarIndicator extends LinearLayout implements View.OnClickListener, ViewPager.OnPageChangeListener {    private static final String TAG = "CaterpillarIndicator";    private static final int BASE_ID = 0xffff00;    private static final int FOOTER_COLOR = 0xFFFFC445;    private static final int ITEM_TEXT_COLOR_NORMAL = 0xFF999999;    private static final int ITEM_TEXT_COLOR_SELECT = 0xFFFFC445;    private boolean isRoundRectangleLine = true;    private boolean isCaterpillar = true;    private int mFootLineColor;    private int mTextSizeNormal;    private int mTextSizeSelected;    private int mTextColorNormal;    private int mTextColorSelected;    private int mFooterLineHeight;    private int mItemLineWidth;    /**     * item count     */    private int mItemCount = 0;    private int mCurrentScroll = 0;    private int mSelectedTab = 0;    private boolean isNewClick;    /**     * item view id index     */    private int mCurrID = 0;    private List<TitleInfo> mTitles;    private ViewPager mViewPager;    /**     * indicator line is Rounded Rectangle     */    /**     * line paint     */    private Paint mPaintFooterLine;    /**     * line RectF     */    private RectF drawLineRect;    public CaterpillarIndicator(Context context) {        this(context, null);    }    public CaterpillarIndicator(Context context, AttributeSet attrs) {        super(context, attrs);        setFocusable(true);        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CaterpillarIndicator);        mFootLineColor = a.getColor(R.styleable.CaterpillarIndicator_slide_footer_color, FOOTER_COLOR);        mTextSizeNormal = a.getDimensionPixelSize(R.styleable.CaterpillarIndicator_slide_text_size_normal, dip2px(mTextSizeNormal));        mTextSizeSelected = a.getDimensionPixelSize(R.styleable.CaterpillarIndicator_slide_text_size_selected, dip2px(mTextSizeNormal));        mFooterLineHeight = a.getDimensionPixelOffset(R.styleable.CaterpillarIndicator_slide_footer_line_height, dip2px(3));        mTextColorSelected = a.getColor(R.styleable.CaterpillarIndicator_slide_text_color_selected, ITEM_TEXT_COLOR_SELECT);        mTextColorNormal = a.getColor(R.styleable.CaterpillarIndicator_slide_text_color_normal, ITEM_TEXT_COLOR_NORMAL);        isCaterpillar = a.getBoolean(R.styleable.CaterpillarIndicator_slide_caterpillar, true);        isRoundRectangleLine = a.getBoolean(R.styleable.CaterpillarIndicator_slide_round, true);        mItemLineWidth = (int) a.getDimension(R.styleable.CaterpillarIndicator_slide_item_width, dip2px(24));        setWillNotDraw(false);        initDraw();        a.recycle();    }    /**     * set foot line height     *     * @param mFooterLineHeight foot line height (int)     */    public void setFooterLineHeight(int mFooterLineHeight) {        this.mFooterLineHeight = dip2px(mFooterLineHeight);        invalidate();    }    /**     * item width     *     * @param mItemLineWidth item width(dp)     */    public void setItemLineWidth(int mItemLineWidth) {        this.mItemLineWidth = dip2px(mItemLineWidth);        invalidate();    }    public void setCaterpillar(boolean caterpillar) {        isCaterpillar = caterpillar;        invalidate();    }    /**     * is round line     *     * @param roundRectangleLine true (yes) false (no )     */    public void setRoundRectangleLine(boolean roundRectangleLine) {        isRoundRectangleLine = roundRectangleLine;    }    private void initDraw() {        mPaintFooterLine = new Paint();        mPaintFooterLine.setAntiAlias(true);        mPaintFooterLine.setStyle(Paint.Style.FILL);        drawLineRect = new RectF(0, 0, 0, 0);    }    @Override    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {        float a = (float) getWidth() / (float) mViewPager.getWidth();        onScrolled((int) ((getWidth() + mViewPager.getPageMargin()) * position + positionOffsetPixels * a));    }    @Override    public void onPageSelected(int position) {        onSwitched(position);    }    @Override    public void onPageScrollStateChanged(int state) {    }    public void setFootLineColor(int mFootLineColor) {        this.mFootLineColor = mFootLineColor;        invalidate();    }    /**     * set text normal size(dp)     *     * @param mTextSizeNormal normal text size     */    public void setTextSizeNormal(int mTextSizeNormal) {        this.mTextSizeNormal = dip2px(mTextSizeNormal);        updateItemText();    }    public void setTextSizeSelected(int mTextSizeSelected) {        this.mTextSizeSelected = dip2px(mTextSizeSelected);        updateItemText();    }    public void setTextColorNormal(int mTextColorNormal) {        this.mTextColorNormal = mTextColorNormal;        updateItemText();    }    public void setTextColorSelected(int mTextColorSelected) {        this.mTextColorSelected = mTextColorSelected;        updateItemText();    }    private void updateItemText() {        for (int i = 0; i < getChildCount(); i++) {            View v = getChildAt(i);            if (v instanceof TextView) {                TextView tv = (TextView) v;                if (tv.isSelected()) {                    tv.setTextColor(mTextColorSelected);                    tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSizeSelected);                } else {                    tv.setTextColor(mTextColorNormal);                    tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSizeNormal);                }            }        }    }    private int dip2px(float dpValue) {        final float scale = Resources.getSystem().getDisplayMetrics().density;        return (int) (dpValue * scale + 0.5f);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (isNewClick) {            isNewClick = false;            return;        }        float scroll_x;        int cursorWidth;        if (mItemCount != 0) {            cursorWidth = getWidth() / mItemCount;            scroll_x = (mCurrentScroll - ((mSelectedTab) * (getWidth()))) / mItemCount;        } else {            cursorWidth = getWidth();            scroll_x = mCurrentScroll;        }        this.mItemLineWidth = mItemLineWidth > cursorWidth ? cursorWidth : mItemLineWidth;        int mItemLeft;        int mItemRight;        if (mItemLineWidth < cursorWidth) {            mItemLeft = (cursorWidth - mItemLineWidth) / 2;            mItemRight = cursorWidth - mItemLeft;        } else {            mItemLeft = 0;            mItemRight = cursorWidth;        }        float leftX;        float rightX;        boolean isHalf = Math.abs(scroll_x) < (cursorWidth / 2);        mPaintFooterLine.setColor(mFootLineColor);        if (isCaterpillar) {            if (scroll_x < 0) {                if (isHalf) {                    leftX = (mSelectedTab) * cursorWidth + scroll_x * 2 + mItemLeft;                    rightX = (mSelectedTab) * cursorWidth + mItemRight;                } else {                    leftX = (mSelectedTab - 1) * cursorWidth + mItemLeft;                    rightX = (mSelectedTab) * cursorWidth + mItemRight + (scroll_x + (cursorWidth / 2)) * 2;                }            } else if (scroll_x > 0) {                if (isHalf) {                    leftX = mSelectedTab * cursorWidth + mItemLeft;                    rightX = (mSelectedTab) * cursorWidth + mItemRight + scroll_x * 2;                } else {                    leftX = mSelectedTab * cursorWidth + mItemLeft + (scroll_x - (cursorWidth / 2)) * 2;                    rightX = (mSelectedTab + 1) * cursorWidth + mItemRight;                }            } else {                leftX = mSelectedTab * cursorWidth + mItemLeft;                rightX = mSelectedTab * cursorWidth + mItemRight;            }        } else {            leftX = mSelectedTab * cursorWidth + scroll_x + mItemLeft;            rightX = (mSelectedTab) * cursorWidth + scroll_x + mItemRight;        }        float bottomY = getHeight();        //set foot line height        float topY = bottomY - mFooterLineHeight;        drawLineRect.left = leftX;        drawLineRect.right = rightX;        drawLineRect.bottom = bottomY;        drawLineRect.top = topY;        int roundXY = isRoundRectangleLine ? (mFooterLineHeight / 2) : 0;        canvas.drawRoundRect(drawLineRect, roundXY, roundXY, mPaintFooterLine);    }    private void onScrolled(int h) {        mCurrentScroll = h;        Log.e(TAG, "onScrolled: onScrolled"+mCurrentScroll);        invalidate();    }    public synchronized void onSwitched(int position) {        if (mSelectedTab == position) {            return;        }        setCurrentTab(position);    }    /**     * init indication     *     * @param startPos   init select pos     * @param tabs       title list     * @param mViewPager ViewPage     */    public void init(int startPos, List<TitleInfo> tabs, ViewPager mViewPager) {        removeAllViews();        this.mViewPager = mViewPager;        this.mViewPager.addOnPageChangeListener(this);        this.mTitles = tabs;        this.mItemCount = tabs.size();        setWeightSum(mItemCount);        for (int i = 0; i < mItemCount; i++) {            add(tabs.get(i).getName());        }        mViewPager.setCurrentItem(startPos);        invalidate();        requestLayout();    }    protected void add(String label) {        TextView view = new TextView(getContext());        LinearLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1);        view.setGravity(Gravity.CENTER);        view.setLayoutParams(params);        view.setText(label);        view.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSizeNormal);        view.setTextColor(mTextColorNormal);        view.setId(BASE_ID + (mCurrID++));        view.setOnClickListener(this);        addView(view);    }    @Override    public void onClick(View v) {        int position = v.getId() - BASE_ID;        isNewClick = true;        mViewPager.setCurrentItem(position, true);    }    /**     * get title list size     *     * @return list size     */    public int getTitleCount() {        return mTitles != null ? mTitles.size() : 0;    }    public synchronized void setCurrentTab(int index) {        if (index < 0 || index >= getTitleCount()) {            return;        }        View oldTab = getChildAt(mSelectedTab);        if (oldTab != null) {            oldTab.setSelected(false);            setTabTextSize(oldTab, false);        }        mSelectedTab = index;        View newTab = getChildAt(mSelectedTab);        if (newTab != null) {            setTabTextSize(newTab, true);            newTab.setSelected(true);        }    }    /**     * set select textView textSize&textColor state     *     * @param tab      TextView     * @param selected is Select     */    private void setTabTextSize(View tab, boolean selected) {        if (tab instanceof TextView) {            TextView tv = (TextView) tab;            tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, selected ? mTextSizeSelected : mTextSizeNormal);            tv.setTextColor(selected ? mTextColorSelected : mTextColorNormal);        }    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        if (mCurrentScroll == 0 && mSelectedTab != 0) {            mCurrentScroll = (getWidth() + mViewPager.getPageMargin()) * mSelectedTab;        }    }    /**     * title     */    public static class TitleInfo {        String name;        public TitleInfo(String name) {            this.name = name;        }        public String getName() {            return name;        }        public void setName(String name) {            this.name = name;        }    }}
1 0
原创粉丝点击