利用 viewPager ,ShapeDrawable 实现带小圆球的页面滑动

来源:互联网 发布:wamp php.ini在哪 编辑:程序博客网 时间:2024/04/25 13:50

首先声明文章的代码来自于 github 上的开源库,但是因为下载后的时间较长。没有去逐个寻找其出处。文中修改了小许的代码,并为文中的代码加了些个人的理解(注释)。具体的如代码中所示。关于 ShapeDrawable 可以参见上一篇博客:http://blog.csdn.net/antimage08/article/details/50373159。

运行效果如下(其背景颜色是随机生成的):



定义属性资源 attrs.xml :

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="CircleDisplay">        <attr name="ci_radius" format="dimension"/>        <attr name="ci_margin" format="dimension"/>        <attr name="ci_background" format="color|integer"/>        <attr name="ci_selected_background" format="color|integer"/>        <attr name="ci_gravity">            <enum name="left" value="0"/>            <enum name="center" value="1"/>            <enum name="right" value="2"/>        </attr>        <attr name="ci_mode">            <enum name="inside" value="0"/>            <enum name="outside" value="1"/>            <enum name="solo" value="2"/>        </attr>    </declare-styleable></resources>




content_main.xml :
<?xml version="1.0" encoding="utf-8"?><RelativeLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:CircleDisplay="http://schemas.android.com/apk/com.crazy.circle"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    app:layout_behavior="@string/appbar_scrolling_view_behavior"    tools:context="com.crazy.circle.MainActivity"    tools:showIn="@layout/activity_main">    <android.support.v4.view.ViewPager        android:id="@+id/viewpager"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_gravity="center" />    <com.crazy.circledisplay.CircleDisplay        android:id="@+id/indicator"        android:layout_width="match_parent"        android:layout_height="40dp"        android:layout_centerVertical="true"        CircleDisplay:ci_background="@android:color/white"        CircleDisplay:ci_gravity="center"        CircleDisplay:ci_margin="5dp"        CircleDisplay:ci_mode="outside"        CircleDisplay:ci_radius="10dp"        CircleDisplay:ci_selected_background="0xffe6454a" /></RelativeLayout>




ShapeHolder.java :
package com.crazy.circledisplay;import android.graphics.Paint;import android.graphics.drawable.ShapeDrawable;import android.graphics.drawable.shapes.Shape;/** *  该类主要用于提供二维图形(包含多个小圆球,提供给 CircleDisplay 类) */public class ShapeHolder {    private float x = 0, y = 0;    private ShapeDrawable shape;    private int color;    private float alpha = 1f;    private Paint paint;    public ShapeHolder(ShapeDrawable s) {        shape = s;    }    public void setPaint(Paint value) {        paint = value;    }    public Paint getPaint() {        return paint;    }    public void setX(float value) {        x = value;    }    public float getX() {        return x;    }    public void setY(float value) {        y = value;    }    public float getY() {        return y;    }    public void setShape(ShapeDrawable value) {        shape = value;    }    public ShapeDrawable getShape() {        return shape;    }    public int getColor() {        return color;    }    public void setColor(int value) {        shape.getPaint().setColor(value);        color = value;    }    public void setAlpha(float alpha) {        this.alpha = alpha;        shape.setAlpha((int) ((alpha * 255f) + .5f));    }    public float getWidth() {        return shape.getShape().getWidth();    }    public void setWidth(float width) {        Shape s = shape.getShape();        s.resize(width, s.getHeight());    }    public float getHeight() {        return shape.getShape().getHeight();    }    public void setHeight(float height) {        Shape s = shape.getShape();        s.resize(s.getWidth(), height);    }    /**     * 其中的 resize( width,height) 必须在 onDraw()之前调用     */    public void resizeShape(final float width,final float height){        shape.getShape().resize(width,height);    }}




MainActivity.java :

package com.crazy.circledisplay;import android.os.Bundle;import android.support.v4.view.PagerAdapter;import android.support.v4.view.ViewPager;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.View;import android.view.ViewGroup;import java.util.ArrayList;import java.util.List;import java.util.Random;public class MainActivity extends AppCompatActivity {    private List<View> viewList;    private ViewPager viewPager;    private CircleDisplay circleDisplay;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);        setSupportActionBar(toolbar);        initData();        viewPager = (ViewPager) findViewById(R.id.viewpager);        viewPager.setAdapter(pagerAdapter);        circleDisplay = (CircleDisplay) findViewById(R.id.indicator);        // 让小圆球与 ViewPager 关联起来        circleDisplay.setViewPager(viewPager);    }    /**     *  初始化,随机得到背景颜色     */    private void initData(){        viewList = new ArrayList<View>();        Random random = new Random();        for(int i=0;i<5;i++){            View view = new View(this);            view.setBackgroundColor(0xff000000| random.nextInt(0x00ffffff));            viewList.add(view);        }    }    // 关于 PagerAdapter 可以参考前文    PagerAdapter pagerAdapter = new PagerAdapter() {        @Override        public boolean isViewFromObject(View arg0, Object arg1) {            return arg0 == arg1;        }        @Override        public int getCount() {            return viewList.size();        }        @Override        public void destroyItem(ViewGroup container, int position,                                Object object) {            container.removeView(viewList.get(position));        }        @Override        public int getItemPosition(Object object) {            return super.getItemPosition(object);        }        @Override        public CharSequence getPageTitle(int position) {            return "title";        }        @Override        public Object instantiateItem(ViewGroup container, int position) {            container.addView(viewList.get(position));            return viewList.get(position);        }    };}




CircleDisplay.java :

package com.crazy.circledisplay;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.drawable.ShapeDrawable;import android.graphics.drawable.shapes.OvalShape;import android.support.v4.view.ViewPager;import android.util.AttributeSet;import android.view.View;import java.util.ArrayList;import java.util.List;public class CircleDisplay extends View{    private ViewPager viewPager;    // 小圆球的集合    private List<ShapeHolder> tabItems;    private ShapeHolder movingItem;    // 属性资源    private int mCurItemPosition;    private float mCurItemPositionOffset;    private float mIndicatorRadius;    private float mIndicatorMargin;    private int mIndicatorBackground;    private int mIndicatorSelectedBackground;    private Gravity mIndicatorLayoutGravity;    private Mode mIndicatorMode;    // 设置园的半径    private final int DEFAULT_RADIUS = 15;    // 设置两个圆之间的距离    private final int DEFAULT_MARGIN = 40;    // 设置圆的颜色(未选中时)    private final int DEFAULT_BACKGROUND = Color.WHITE;    // 设置选中时圆的颜色    private final int DEFAULT_SELECTED_BACKGROUND = Color.RED;    // 设置显示的位置(水平方向)    private final int DEFAULT_LAYOUT_GRAVITY = Gravity.CENTER.ordinal();    private final int DEFAULT_MODE = Mode.SOLO.ordinal();    public enum Gravity{        LEFT,       // 左        CENTER,     // 中        RIGHT       // 右    }    public enum Mode{        INSIDE,        OUTSIDE,        SOLO    }    public CircleDisplay(Context context) {        this(context, null);    }    public CircleDisplay(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public CircleDisplay(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(context, attrs);    }    private void init(Context context,AttributeSet attrs){        tabItems = new ArrayList<>();        handleTypedArray(context, attrs);    }    /**     *  检索从 atrrs.xml 这个结构对应于给定的属性 obtainstyledattributes 的位置     * @param context     * @param attrs     */    private void handleTypedArray(Context context, AttributeSet attrs) {        if(attrs == null)            return;        TypedArray typedArray = context.obtainStyledAttributes(                attrs, R.styleable.CircleDisplay);        // 获取半径参数        mIndicatorRadius = typedArray.getDimensionPixelSize(                R.styleable.CircleDisplay_ci_radius,                DEFAULT_RADIUS);        mIndicatorMargin = typedArray.getDimensionPixelSize(                R.styleable.CircleDisplay_ci_margin,                DEFAULT_MARGIN);        mIndicatorBackground = typedArray.getColor(                R.styleable.CircleDisplay_ci_background,                DEFAULT_BACKGROUND);        mIndicatorSelectedBackground = typedArray.getColor(                R.styleable.CircleDisplay_ci_selected_background,                DEFAULT_SELECTED_BACKGROUND);        int gravity = typedArray.getInt(                R.styleable.CircleDisplay_ci_gravity,                DEFAULT_LAYOUT_GRAVITY);        mIndicatorLayoutGravity = Gravity.values()[gravity];        int mode = typedArray.getInt(                R.styleable.CircleDisplay_ci_mode,                DEFAULT_MODE);        mIndicatorMode = Mode.values()[mode];        typedArray.recycle();    }    public void setViewPager(final ViewPager viewPager){        this.viewPager = viewPager;        createTabItems();        createMovingItem();        setUpListener();    }    private void setUpListener() {        viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {            /**             *             * @param position 当前的位置,从 0 开始记录;             * @param positionOffset  位置偏移量,从右往左滑动,数值越来越大(当翻页后数值变为 0.0),             *             但都小于 1.0;从左往右翻动,数值逐渐减小 (当翻页后数值变为 0.0);             * @param positionOffsetPixels 位置偏移像素,从右往左滑动,数值越来越大(当翻页后数值变为 0);             *                             从左往右翻动,数值逐渐减小 (当翻页后数值变为 0)             */            @Override            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {                super.onPageScrolled(position, positionOffset, positionOffsetPixels);                if (mIndicatorMode != Mode.SOLO) {                    trigger(position, positionOffset);                }            }            @Override            public void onPageSelected(int position) {                super.onPageSelected(position);                // 当滑动到下一张图片时,绘制小圆球                if (mIndicatorMode == Mode.SOLO) {                    trigger(position, 0);                }            }        });    }    /**     * 当 ViewPager 的条目改变时,重绘     * @param position     * @param positionOffset     */    private void trigger(int position,float positionOffset){        CircleDisplay.this.mCurItemPosition = position;        CircleDisplay.this.mCurItemPositionOffset = positionOffset;        requestLayout();        invalidate();    }    private void createTabItems() {        for (int i = 0; i < viewPager.getAdapter().getCount(); i++) {            OvalShape circle = new OvalShape();            ShapeDrawable drawable = new ShapeDrawable(circle);            ShapeHolder shapeHolder = new ShapeHolder(drawable);            Paint paint = drawable.getPaint();            paint.setColor(mIndicatorBackground);            // 去锯齿            paint.setAntiAlias(true);            shapeHolder.setPaint(paint);            tabItems.add(shapeHolder);        }    }    private void createMovingItem() {        OvalShape circle = new OvalShape();        ShapeDrawable drawable = new ShapeDrawable(circle);        movingItem = new ShapeHolder(drawable);        Paint paint = drawable.getPaint();        paint.setColor(mIndicatorSelectedBackground);        paint.setAntiAlias(true);        switch (mIndicatorMode){            case INSIDE:                // 设置混合模式 (只在源图像和目标图像相交的地方绘制目标图像)                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));                break;            case OUTSIDE:                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));                break;            case SOLO:                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));                break;        }        movingItem.setPaint(paint);    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        super.onLayout(changed, left, top, right, bottom);        final int width = getWidth();        final int height = getHeight();        layoutTabItems(width, height);        layoutMovingItem(mCurItemPosition, mCurItemPositionOffset);    }    private void layoutTabItems(final int containerWidth,final int containerHeight){        if(tabItems == null){            throw new IllegalStateException("forget to create tabItems?");        }        // y 轴方向上的中点        final float yCoordinate = containerHeight*0.5f;        final float startPosition = startDrawPosition(containerWidth);        for(int i=0;i<tabItems.size();i++){            ShapeHolder item = tabItems.get(i);            item.resizeShape(2* mIndicatorRadius,2* mIndicatorRadius);            item.setY(yCoordinate- mIndicatorRadius);            float x = startPosition + (mIndicatorMargin + mIndicatorRadius*2)*i;            item.setX(x);        }    }    private float startDrawPosition(final int containerWidth){        // 居左时,绘制小圆球起始位置从 0 开始        if(mIndicatorLayoutGravity == Gravity.LEFT)            return 0;        // 所有小圆球的所占位置的宽度(包括小圆球之间的距离)        float tabItemsLength = tabItems.size()*(2* mIndicatorRadius + mIndicatorMargin)- mIndicatorMargin;        // 超出屏幕宽度时,起始位置为 0        if(containerWidth<tabItemsLength){            return 0;        }        // 小圆球整体居中时        if(mIndicatorLayoutGravity == Gravity.CENTER){            // 小圆球到屏幕边缘的最短距离            return (containerWidth-tabItemsLength)/2;        }        return containerWidth - tabItemsLength;    }    private void layoutMovingItem(final int position,final float positionOffset){        if(movingItem == null){            throw new IllegalStateException("forget to create movingItem?");        }        ShapeHolder item = tabItems.get(position);        movingItem.resizeShape(item.getWidth(), item.getHeight());        float x = item.getX()+(mIndicatorMargin + mIndicatorRadius*2)*positionOffset;        movingItem.setX(x);        movingItem.setY(item.getY());    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // saveLayer 可以看成有多个图层(Layer),缺省情况可以看作是只有一个图层Layer.        // 如果需要按层次来绘图,Android的Canvas可以使用SaveLayerXXX, Restore 来创建一些中间层,        // 对于这些Layer是按照“栈结构“来管理的。也就是可以通过修改其属性,能够看到看到底层的视图        int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,                Canvas.MATRIX_SAVE_FLAG |                        Canvas.CLIP_SAVE_FLAG |                        Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |                        Canvas.FULL_COLOR_LAYER_SAVE_FLAG |                        Canvas.CLIP_TO_LAYER_SAVE_FLAG);        for(ShapeHolder item : tabItems){            canvas.save();            canvas.translate(item.getX(),item.getY());            item.getShape().draw(canvas);            canvas.restore();        }        if(movingItem != null){            canvas.save();            canvas.translate(movingItem.getX(), movingItem.getY());            movingItem.getShape().draw(canvas);            canvas.restore();        }        canvas.restoreToCount(sc);    }    // 注释掉的这部分可以扩展其他的功能/*    public void setIndicatorRadius(float mIndicatorRadius) {        this.mIndicatorRadius = mIndicatorRadius;    }    public void setIndicatorMargin(float mIndicatorMargin) {        this.mIndicatorMargin = mIndicatorMargin;    }    public void setIndicatorBackground(int mIndicatorBackground) {        this.mIndicatorBackground = mIndicatorBackground;    }    public void setIndicatorSelectedBackground(int mIndicatorSelectedBackground) {        this.mIndicatorSelectedBackground = mIndicatorSelectedBackground;    }    public void setIndicatorLayoutGravity(Gravity mIndicatorLayoutGravity) {        this.mIndicatorLayoutGravity = mIndicatorLayoutGravity;    }    public void setIndicatorMode(Mode mIndicatorMode) {        this.mIndicatorMode = mIndicatorMode;    }*/}


0 0
原创粉丝点击