精华内容
下载资源
问答
  • 圆形指示器 public class CircleIndicatorView extends View implements ViewPager.OnPageChangeListener{ private static final String LETTER[] = new String[]{"A","B","C","D","E","F","G","H","I","G",...

    添加命名空间

    xmlns:qishui="http://schemas.android.com/apk/res-auto"

    使用控件布局

    <com.example.zhou.tabpageindicator.view.ViewPagerIndicator
        android:id="@+id/id_indicator"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:background="@color/colorPrimary"
        android:orientation="horizontal"
        qishui:item_count="5">
    </com.example.zhou.tabpageindicator.view.ViewPagerIndicator>
    <android.support.v4.view.ViewPager
        android:id="@+id/id_vp"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    </android.support.v4.view.ViewPager>

    逻辑设置

    final List<String> mDatas = Arrays.asList(UiUtils.getStringArray(R.array.tab_names));
    mAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
        @Override
        public int getCount() {
            return mDatas.size();
        }
    
        @Override
        public Fragment getItem(int position) {
            return FragmentFactory.createFragment(position);
        }
    };
    //设置Tab上的标题
    mIndicator.setTabItemTitles(mDatas);
    mViewPager.setAdapter(mAdapter);
    //设置关联的ViewPager
    mIndicator.setViewPager(mViewPager, 0);

    其中mDatas获取到的是一串在Strings.xml字符集合

    <string-array name="tab_names">
        <item>首页</item>
        <item>活动</item>
        <item>辅练</item>
        <item>商城</item>
        <item>我的</item>
    </string-array>

    简单点可以直接定义出来,不必获取xml中的;其中FragmentFactory就是创建不同的fragment简单点new 一个定义好的即可。

    此文只为记录,向hongyang大佬致敬

    http://blog.csdn.net/lmj623565791/article/details/42160391

    相关资料下载

    http://pan.baidu.com/s/1slFbxF3

     

    圆形指示器

    public class CircleIndicatorView extends View implements ViewPager.OnPageChangeListener{
        private static final String LETTER[] = new String[]{"A","B","C","D","E","F","G","H","I","G","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"};
        // private int mSelectColor = Color.parseColor("#E38A7C");
        private int mSelectColor = Color.parseColor("#FFFFFF");
        private Paint mCirclePaint;
        private Paint mTextPaint;
        private int mCount; // indicator 的数量
        private int mRadius;//半径
        private int mStrokeWidth;//border
        private int mTextColor;// 小圆点中文字的颜色
        private int mDotNormalColor;// 小圆点默认颜色
        private int mSpace = 0;// 圆点之间的间距
        private List<Indicator> mIndicators;
        private int mSelectPosition = 0; // 选中的位置
        private FillMode mFillMode = FillMode.NONE;// 默认只有小圆点
        private ViewPager mViewPager;
        private OnIndicatorClickListener mOnIndicatorClickListener;
        /**
         * 是否允许点击Indicator切换ViewPager
         */
        private boolean mIsEnableClickSwitch = false;
        public CircleIndicatorView(Context context) {
            super(context);
            init();
        }
    
        public CircleIndicatorView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            getAttr(context,attrs);
            init();
        }
    
        public CircleIndicatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            getAttr(context,attrs);
            init();
        }
    
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        public CircleIndicatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
            getAttr(context,attrs);
            init();
        }
    
        private void init(){
    
            mCirclePaint = new Paint();
            mCirclePaint.setDither(true);
            mCirclePaint.setAntiAlias(true);
            mCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
    
            mTextPaint = new Paint();
            mTextPaint.setDither(true);
            mTextPaint.setAntiAlias(true);
            // 默认值
            mIndicators = new ArrayList<>();
    
            initValue();
    
        }
    
        private void initValue(){
            mCirclePaint.setColor(mDotNormalColor);
            mCirclePaint.setStrokeWidth(mStrokeWidth);
    
            mTextPaint.setColor(mTextColor);
            mTextPaint.setTextSize(mRadius);
        }
    
        /**
         * 获取自定义属性
         * @param context
         * @param attrs
         */
        private void getAttr(Context context,AttributeSet attrs){
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicatorView);
            mRadius = (int) typedArray.getDimensionPixelSize(R.styleable.CircleIndicatorView_indicatorRadius,dpToPx(6));
            mStrokeWidth = (int) typedArray.getDimensionPixelSize(R.styleable.CircleIndicatorView_indicatorBorderWidth,dpToPx(2));
            mSpace = typedArray.getDimensionPixelSize(R.styleable.CircleIndicatorView_indicatorSpace,dpToPx(5));
            // color
            mTextColor = typedArray.getColor(R.styleable.CircleIndicatorView_indicatorTextColor,Color.BLACK);
            mSelectColor = typedArray.getColor(R.styleable.CircleIndicatorView_indicatorSelectColor,Color.WHITE);
            mDotNormalColor = typedArray.getColor(R.styleable.CircleIndicatorView_indicatorColor,Color.GRAY);
    
            mIsEnableClickSwitch = typedArray.getBoolean(R.styleable.CircleIndicatorView_enableIndicatorSwitch,false);
            int fillMode = typedArray.getInt(R.styleable.CircleIndicatorView_fill_mode,2);
            if(fillMode == 0){
                mFillMode = FillMode.LETTER;
            }else if(fillMode == 1){
                mFillMode = FillMode.NUMBER;
            }else{
                mFillMode = FillMode.NONE;
            }
            typedArray.recycle();
        }
    
        /**
         * 测量每个圆点的位置
         */
        private void measureIndicator(){
            mIndicators.clear();
            float cx = 0;
            for(int i=0;i<mCount;i++){
                Indicator indicator = new Indicator();
                if( i== 0){
                    cx = mRadius + mStrokeWidth;
                }else{
                    cx += (mRadius + mStrokeWidth) * 2 +mSpace;
                }
    
                indicator.cx = cx;
                indicator.cy = getMeasuredHeight() / 2;
    
                mIndicators.add(indicator);
            }
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
            int width = (mRadius+mStrokeWidth)* 2 * mCount + mSpace *(mCount - 1);
            int height = mRadius * 2 + mSpace * 2;
    
            setMeasuredDimension(width,height);
    
            measureIndicator();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
    
            for(int i=0;i<mIndicators.size();i++){
    
                Indicator indicator = mIndicators.get(i);
                float x = indicator.cx;
    
                float y = indicator.cy;
    
                if(mSelectPosition == i){
                    mCirclePaint.setStyle(Paint.Style.FILL);
                    mCirclePaint.setColor(mSelectColor);
                }else{
                    mCirclePaint.setColor(mDotNormalColor);
                    if(mFillMode != FillMode.NONE){
                        mCirclePaint.setStyle(Paint.Style.STROKE);
                    }else{
                        mCirclePaint.setStyle(Paint.Style.FILL);
    
                    }
                }
                canvas.drawCircle(x,y, mRadius, mCirclePaint);
    
                // 绘制小圆点中的内容
                if(mFillMode != FillMode.NONE){
                    String text = "";
                    if(mFillMode == FillMode.LETTER){
                        if(i >= 0 && i<LETTER.length){
                            text = LETTER[i];
                        }
                    }else{
                        text = String.valueOf(i+1);
                    }
                    Rect bound = new Rect();
                    mTextPaint.getTextBounds(text,0,text.length(),bound);
                    int textWidth = bound.width();
                    int textHeight = bound.height();
    
                    float textStartX = x - textWidth / 2;
                    float textStartY = y + textHeight / 2;
                    canvas.drawText(text,textStartX,textStartY, mTextPaint);
                }
    
            }
    
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float xPoint = 0;
            float yPoint = 0;
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    xPoint = event.getX();
                    yPoint = event.getY();
                    handleActionDown(xPoint,yPoint);
                    break;
    
            }
    
            return super.onTouchEvent(event);
        }
    
        private void handleActionDown(float xDis,float yDis){
            for(int i=0;i<mIndicators.size();i++){
                Indicator indicator = mIndicators.get(i);
                if(xDis < (indicator.cx + mRadius+mStrokeWidth)
                        && xDis >=(indicator.cx - (mRadius + mStrokeWidth))
                        && yDis >= (yDis - (indicator.cy+mStrokeWidth))
                        && yDis <(indicator.cy+mRadius+mStrokeWidth)){
                    // 找到了点击的Indicator
                    // 是否允许切换ViewPager
                    if(mIsEnableClickSwitch){
                        mViewPager.setCurrentItem(i,false);
                    }
    
                    // 回调
                    if(mOnIndicatorClickListener!=null){
                        mOnIndicatorClickListener.onSelected(i);
                    }
                    break;
                }
            }
        }
    
        public void setOnIndicatorClickListener(OnIndicatorClickListener onIndicatorClickListener) {
            mOnIndicatorClickListener = onIndicatorClickListener;
        }
    
        private void setCount(int count) {
            mCount = count;
            invalidate();
        }
    
        /**
         * 设置 border
         * @param borderWidth
         */
        public void setBorderWidth(int borderWidth){
            mStrokeWidth = borderWidth;
            initValue();
        }
    
        /**
         * 设置文字的颜色
         * @param textColor
         */
        public void setTextColor(int textColor) {
            mTextColor = textColor;
            initValue();
        }
    
        /**
         * 设置选中指示器的颜色
         * @param selectColor
         */
        public void setSelectColor(int selectColor) {
            mSelectColor = selectColor;
        }
    
        /**
         *  设置指示器默认颜色
         * @param dotNormalColor
         */
        public void setDotNormalColor(int dotNormalColor) {
            mDotNormalColor = dotNormalColor;
            initValue();
        }
    
        /**
         * 设置选中的位置
         * @param selectPosition
         */
        public void setSelectPosition(int selectPosition) {
            mSelectPosition = selectPosition;
        }
    
        /**
         * 设置Indicator 模式
         * @param fillMode
         */
        public void setFillMode(FillMode fillMode) {
            mFillMode = fillMode;
        }
    
        /**
         * 设置Indicator 半径
         * @param radius
         */
        public void setRadius(int radius) {
            mRadius = radius;
            initValue();
        }
    
        public void setSpace(int space) {
            mSpace = space;
        }
    
        public void setEnableClickSwitch(boolean enableClickSwitch){
            mIsEnableClickSwitch = enableClickSwitch;
        }
        /**
         *  与ViewPager 关联
         * @param viewPager
         */
        public void setUpWithViewPager(ViewPager viewPager){
            releaseViewPager();
            if(viewPager == null){
                return;
            }
            mViewPager = viewPager;
            mViewPager.addOnPageChangeListener(this);
            int count = mViewPager.getAdapter().getCount();
            setCount(count);
        }
    
        /**
         * 重置ViewPager
         */
        private void releaseViewPager(){
            if(mViewPager!=null){
                mViewPager.removeOnPageChangeListener(this);
                mViewPager = null;
            }
    
        }
    
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
        }
    
        @Override
        public void onPageSelected(int position) {
            mSelectPosition = position;
            invalidate();
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
    
        }
    
        /**
         * Indicator 点击回调
         */
        public interface OnIndicatorClickListener{
            public void onSelected(int position);
        }
    
    
        public static class Indicator{
            public float cx; // 圆心x坐标
            public float cy; // 圆心y 坐标
        }
    
        public enum FillMode{
            LETTER,
            NUMBER,
            NONE
        }
    
    
        public  int dpToPx(int dp) {
            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
        }
    
        public  int pxToDp(float px) {
            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, px, Resources.getSystem().getDisplayMetrics());
        }
    
    }

    自定义属性

    <declare-styleable name="CircleIndicatorView">
        <attr name="indicatorRadius" format="dimension"/>
        <attr name="indicatorBorderWidth" format="dimension"/>
        <attr name="indicatorSpace" format="dimension"/>
        <attr name="indicatorTextColor" format="color"/>
        <attr name="indicatorColor" format="color"/>
        <attr name="indicatorSelectColor" format="color"/>
        <attr name="enableIndicatorSwitch" format="boolean"/>
        <attr name="fill_mode">
            <enum name="letter" value="0"/>
            <enum name="number" value="1"/>
            <enum name="none" value="2"/>
        </attr>
    </declare-styleable>

    xml设置

    <com.zhouwei.indicatorview.CircleIndicatorView
             android:id="@+id/indicator_view"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentBottom="true"
             android:layout_marginBottom="50dp"
             android:layout_centerHorizontal="true"
             app:indicatorSelectColor="#00A882"
             app:fill_mode="letter"
             app:indicatorBorderWidth="2dp"
             app:indicatorRadius="8dp"
             app:indicatorColor="@color/colorAccent"
             app:indicatorTextColor="@android:color/white"
             />

    .class设置

    // 初始化ViewPager 相关
            mViewPager = (ViewPager) findViewById(R.id.viewpager);
            mPagerAdapter = new ViewPagerAdapter();
            mViewPager.setAdapter(mPagerAdapter);
    
            mIndicatorView = (CircleIndicatorView) findViewById(R.id.indicator_view);
            // 关联ViewPager 
            mIndicatorView.setUpWithViewPager(mViewPager);

    相关设置可以不在xml中设置,在代码中同样可以设置

    // 在代码中设置相关属性
            
            CircleIndicatorView indicatorView = (CircleIndicatorView) findViewById(R.id.indicator_view3);
            // 设置半径
            indicatorView.setRadius(DisplayUtils.dpToPx(15));
            // 设置Border
            indicatorView.setBorderWidth(DisplayUtils.dpToPx(2));
    
            // 设置文字颜色
            indicatorView.setTextColor(Color.WHITE);
            // 设置选中颜色
            indicatorView.setSelectColor(Color.parseColor("#FEBB50"));
            //
            indicatorView.setDotNormalColor(Color.parseColor("#E38A7C"));
            // 设置指示器间距
            indicatorView.setSpace(DisplayUtils.dpToPx(10));
            // 设置模式
            indicatorView.setFillMode(CircleIndicatorView.FillMode.LETTER);
            
            // 设置点击Indicator可以切换ViewPager
            indicatorView.setEnableClickSwitch(true);
    
            // 最重要的一步:关联ViewPager
            indicatorView.setUpWithViewPager(mViewPager);

    自定义属性

    属性名 属性意义 取值
    indicatorRadius 设置指示器圆点的半径 单位为 dp 的值
    indicatorBorderWidth 设置指示器的border 单位为 dp 的值
    indicatorSpace 设置指示器之间的距离 单位为 dp 的值
    indicatorTextColor 设置指示器中间的文字颜色 颜色值,如:#FFFFFF
    indicatorColor 设置指示器圆点的颜色 颜色值
    indicatorSelectColor 设置指示器选中的颜色 颜色值
    fill_mode 设置指示器的模式 枚举值:有三种,分别是letter,number和none
    enableIndicatorSwitch 设置是否点击Indicator切换ViewPager,默认为false 布尔值

    源代码参见

    https://github.com/pinguo-zhouwei/CircleIndicatorView

     

     

    转载于:https://my.oschina.net/u/3015461/blog/916382

    展开全文
  • 其实我这个就是实现四个点的效果,通过滑动viewpager来选中当前的点进行变色类似这种效果图,颜色可以自己修改还有大小不多说直接上代码自定义属性xml&lt;?xml version="1.0" encoding="utf-8&...

    其实我这个就是实现四个点的效果,通过滑动viewpager来选中当前的点进行变色

    类似这种效果图,颜色可以自己修改还有大小

    不多说直接上代码

    自定义属性xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="PagePointView">
            <attr name="circleInterval" format="dimension"/>
            <attr name="circleSizi" format="dimension"/>
            <attr name="circleNumber" format="integer"/>
            <attr name="selectIndex" format="integer"/>
            <attr name="circleSelectColor" format="color"/>
            <attr name="circleUnSelectColor" format="color"/>
        </declare-styleable>
    </resources>

    代码

    public class PagePointView extends View {
        private Paint mPaint;
        private int mCircleInterval;//设置圆和点圆之间的间隔
        private int mCircleRadius;//设置圆的大小
        private int mCircleNumber;//设置圆的个数
        private int mSelectIndex = 1;//设置选中的圆
        private int mScreenWidht;
        private int mScreenHeight;
        private int mWidht;
        private int mHeight;
        private int mSelectColor;
        private int mUnSelectColor;
    
    
        public void setmCircleNumber(int mCircleNumber) {
            this.mCircleNumber = mCircleNumber;
        }
    
        public void setIndex(int selectIndex) {
            if (selectIndex > mCircleNumber) {
                this.mSelectIndex = mCircleNumber;
            } else {
                this.mSelectIndex = selectIndex;
            }
            postInvalidate();
        }
    
        public void setmSelectColor(int mSelectColor) {
            this.mSelectColor = mSelectColor;
        }
    
        public void setmUnSelectColor(int mUnSelectColor) {
            this.mUnSelectColor = mUnSelectColor;
        }
    
        public PagePointView(Context context) {
            this(context, null);
        }
    
        public PagePointView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public PagePointView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.BLACK);
            mPaint.setStrokeWidth(20);
            mPaint.setStyle(Paint.Style.FILL);
            mScreenWidht = context.getResources().getDisplayMetrics().widthPixels;
            mScreenHeight = context.getResources().getDisplayMetrics().heightPixels;
    
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PagePointView, defStyleAttr, 0);
            int count = ta.getIndexCount();
            for (int i = 0; i < count; i++) {
                int index = ta.getIndex(i);
                switch (index) {
                    case R.styleable.PagePointView_circleInterval:
                        mCircleInterval = ta.getDimensionPixelSize(index, (int) TypedValue.applyDimension
                                (TypedValue.COMPLEX_UNIT_PX, 12, context.getResources().getDisplayMetrics()));
                        break;
                    case R.styleable.PagePointView_circleNumber:
                        mCircleNumber = ta.getInt(index, 4);
                        break;
                    case R.styleable.PagePointView_circleSizi:
                        mCircleRadius = ta.getDimensionPixelOffset(index, (int) TypedValue.applyDimension
                                (TypedValue.COMPLEX_UNIT_DIP, 12, context.getResources().getDisplayMetrics()));
                        break;
                    case R.styleable.PagePointView_selectIndex:
                        mSelectIndex = ta.getInt(index, 1);
                        break;
                    case R.styleable.PagePointView_circleSelectColor:
                        mSelectColor = ta.getColor(index, Color.WHITE);
                        break;
                    case R.styleable.PagePointView_circleUnSelectColor:
                        mUnSelectColor = ta.getColor(index, Color.BLACK);
                        break;
                }
            }
            ta.recycle();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int wMode = MeasureSpec.getMode(widthMeasureSpec);
            int wSize = MeasureSpec.getSize(widthMeasureSpec);
            int hMode = MeasureSpec.getMode(heightMeasureSpec);
            int hSize = MeasureSpec.getSize(heightMeasureSpec);
            int viewWidht = 0;
            int viewHeight = 0;
            switch (wMode) {
                case MeasureSpec.EXACTLY:
                    viewWidht = wSize;
                    break;
                case MeasureSpec.AT_MOST:
                    viewWidht = mCircleInterval + getPaddingLeft() + getPaddingRight() + mCircleRadius * 2;
                    break;
                case MeasureSpec.UNSPECIFIED:
                    viewWidht = wSize;
                    break;
            }
            switch (hMode) {
                case MeasureSpec.EXACTLY:
                    viewHeight = wSize;
                    break;
                case MeasureSpec.AT_MOST:
                    viewHeight = getPaddingBottom() + getPaddingTop() +  mCircleRadius * 2;
                    break;
                case MeasureSpec.UNSPECIFIED:
                    viewHeight = wSize;
                    break;
            }
            setMeasuredDimension(resolveSize(viewWidht, widthMeasureSpec), resolveSize(viewHeight, heightMeasureSpec));
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mWidht = w;
            mHeight = h;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int mViewWidht;
            for (int i = 1; i <= mCircleNumber; i++) {
                if (mSelectIndex == i) {
                    mPaint.setColor(mSelectColor);
                    canvas.drawCircle((2 * i - 1) * mCircleRadius + mCircleInterval * (i - 1), mCircleRadius, mCircleRadius, mPaint);
                    continue;
                }
                mPaint.setColor(mUnSelectColor);
                canvas.drawCircle((2 * i - 1) * mCircleRadius + mCircleInterval * (i - 1), mCircleRadius, mCircleRadius, mPaint);
            }
        }
    
    
    }

    使用注意:必须在使用的布局xml文件的最外层布局中加上chipChildren=“false”否则只会显示选中的点

    使用在viewpager的监听中设置选中的点位置

    private class PageChangeListener implements ViewPager.OnPageChangeListener {
            @Override
            public void onPageScrollStateChanged(int position) {
    
            }
    
            @Override
            public void onPageScrolled(int position, float arg1, int arg2) {
    
            }
    
            @Override
            public void onPageSelected(int position) {
                // 设置底部小点选中状态
                pagePoint.setIndex(position+1);
            }
    
        }
    可以将上面代码封装到自定义PagePointView中这里没有做过多操作,赖,我的原则能用就行封装后面在做


    展开全文
  • 指示器在APP应用中最为常见,例如新手引导页,滑动页,轮播图等等,在这些界面的指示器中圆形指示器最为常见。圆形指示器在网上一搜也很多,但是很多用着都不太方便,耦合性太强,一些设置往往不符合要求,使用起来...

    指示器在APP应用中最为常见,例如新手引导页,滑动页,轮播图等等,在这些界面的指示器中圆形指示器最为常见。圆形指示器在网上一搜也很多,但是很多用着都不太方便,耦合性太强,一些设置往往不符合要求,使用起来比较麻烦。在之前也写过一个自定义控件,也是指示器,这个指示器其也很优美,但是代码耦合性比较强,有兴趣的同学可以看看,点击打开链接鉴于此准备打造一个万能的轻量级圆形指示器,一行代码就可以实现指示器,同时也可以设置一些常用的属性来制定自己的需求。效果图如下:




    使用

    在使用的时候在xml中引入布局路径,在Activity中直接和ViewPager进行绑定,使用下面的一行代码即可实现上图的指示器功能:

    ((CircleIndicatorView)findViewById(R.id.indicator)).setUpWithViewPager(mViewPager);


    实现原理

    既然是自定义控件,那么离不开自定义控件的几个核心方法:onMeasure,onLayout,onDraw,指示器的自定义也是围绕着这些方法而展开的。首先自定义一些属性,来设置指示器的属性,如半径,间距,颜色等;其次自定义一个类,继承自View,在View中加载相关属性,进行初始化操作;然后我在重写一些View的核心方法,进行指示器参数设置和绘制;最后把View和当前的ViewPager进行绑定,使其联动。接下来介绍具体实现步骤。

    一、自定义属性

    在圆形指示器中常见的属性有默认颜色,选中颜色,半径,间距。因就定义这四大属性。在res/values文件夹下创建attrs.xml文件夹,创建CircleIndicatorView属性。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="CircleIndicatorView">
            <attr name="indicatorRadius" format="dimension"/>
            <attr name="indicatorDistance" format="dimension"/>
            <attr name="indicatorColor" format="color"/>
            <attr name="indicatorSelectColor" format="color"/>
        </declare-styleable>
    </resources>
    二、View的初始化

    1、写一个类CircleIndicatorView继承自View,重写他的构造方法:

        public CircleIndicatorView(Context context) {
            this(context, null);
        }
    
        public CircleIndicatorView(Context context, AttributeSet attrs) {
            this(context, attrs, -1);
        }
    
        public CircleIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            getAttr(context, attrs);
            init();
        }
    2、初始化自定义属性

    这里初始化属性需要借助于Context context, AttributeSet attrs两个参数,AttributeSet里面封装了自定义属性的一些内容。

        private void getAttr(Context context, AttributeSet attrs) {
            TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicatorView);
            radius = attributes.getDimension(R.styleable.CircleIndicatorView_indicatorRadius, DisplayUtils.dpToPx(5));
            distance = attributes.getDimension(R.styleable.CircleIndicatorView_indicatorDistance, DisplayUtils.dpToPx(8));
            defaultColor = attributes.getColor(R.styleable.CircleIndicatorView_indicatorColor, defaultColor);
            defaultSelectColor = attributes.getColor(R.styleable.CircleIndicatorView_indicatorSelectColor, defaultSelectColor);
            attributes.recycle();  //进行变量缓存,每次加载从缓存数据里面读取
        }

    obtainStyledAttributes:是用来加载自定义属性的布局文件,返回的是TypedArray类型的对象

    getDimension:用来获取自定义的属性的大小尺寸,参数一为定义的属性名称,参数二为默认设置的尺寸大小

    getColor:获取自定义颜色类型的数据,参数一为定义的属性名称,参数二为默认设置的颜色

    3、初始化画笔

    初始化自定义属性之后,接下来需要初始化画笔了,设置画笔的一些参数,用来绘制圆形指示器的每个小圆点

        private void init() {
            //创建画笔
            paint = new Paint();
            //设置抗锯齿
            paint.setAntiAlias(true);
            //设置绘制防抖
            paint.setDither(true);
            //设置绘制模式:内部填充:FILL:填充内部,FILL_AND_STROKE:填充内部和描边,STROKE:描边
            paint.setStyle(Paint.Style.FILL);
            //创建集合添加指示器
            indicatorList = new ArrayList<>();
        }

    每个属性的含义都有详细的说明,这里不用多解释。有人可能会疑问问什么不在这里设置颜色呢?如果在这里设置颜色该设置成选中的还是默认的颜色?所以这里设置颜色并不合适,应该在后面选中的状态逻辑判断中,动态设置颜色。

    三、onMeasure
    onMeasuer方法是自定义指示器的核心方法之一,他的主要作用是测量指示器的每个小圆点的绘制位置,因此需要重写onMeasure方法:

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    1、获取指示器的总宽度。
    指示器的宽度是指:每个小圆点的宽度x小圆点的个数+两个小圆点之间距离x(小圆点的个数-1)

    int width = (int) (radius * count * 2 + distance * (count - 1));

    2、获取指示器的高度

    指示器的高度:小圆点的半径x2

    int height = (int) radius * 2;

    3、设置绘制区域的大小

    setMeasuredDimension是设置测量宽高数据的一个方法,接收两个参数分别是宽高。在父类的onMeasure里面可以看到他最终也是调用setMeasuredDimension方法来设置测量区域的大小的。

        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                    getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
        }
    4、封装指示器小圆点的参数

    确定好绘制区域的大小,接下来就需要绘制指示器的每个小圆点,每个小圆点绘制需要一个参考位置,因此封装一个circle对象,里面包含绘制点的宽高

        public static class circle {
            public float x; // 圆心x坐标
            public float y; // 圆心y 坐标
        }
    5、遍历小圆点的个数,确定绘制位置
    在遍历小圆点个数前需要清理掉indicatorList指示器集合,数据为空;在
    遍历每个小圆点的位置之后,需要把每个小圆点参数对象添加到集合里面,以便于绘制小圆点时候每次从中取出。再遍历小圆点个数时候需要确定每个小圆点的坐标值。对于x方向上,随着小圆点的增加每次都是变化的,y轴方法,都是固定的,在y轴水平方向上不变。计算方式如下:

    x轴:上一次绘制的距离+半径x2+小圆点之间的距离

    x += radius * 2 + distance;

    y轴:y周的值为固定的,不受每次x周小圆点的增加而变化,等于半径的长度

    circle.y = getMeasuredHeight() / 2;

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int width = (int) (radius * count * 2 + distance * (count - 1));
            int height = (int) radius * 2;
            setMeasuredDimension(width, height);
            indicatorList.clear();
            float x = 0;
            for (int i = 0; i < count; i++) {
                circle circle = new circle();
                if (i == 0) {
                    x = radius;
                } else {
                    x += radius * 2 + distance;
                }
                circle.x = x;
                circle.y = getMeasuredHeight() / 2;
                indicatorList.add(circle);
            }
        }
    四、onDraw

    完成了测量之后,接下来进行绘制过程,和onMeasure过程一样,需要重写onDraw方法,进行指示器的绘制:

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
        }
    在onDraw方法里面提供一个canvas类,我们可以借助于这个画布进行小圆点的绘制,绘制方法如下:

    canvas.drawCircle(x, y, radius, paint);
    x:表示x轴位置

    y:表示y周位置

    radius:表是绘制的半径

    paint:表示绘制的颜色

    这里我们只需要遍历上一步测量的小圆点的集合indicatorList,然后从中拿去每一个元素的x,y坐标值进行图像绘制。在初始化画笔的时候说到不能再那里设置颜色,现在可以在onDraw方法里面对画笔进行设置颜色值,这样就很灵活。

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            for (int i = 0; i < indicatorList.size(); i++) {
                circle circle = indicatorList.get(i);
                float x = circle.x;
                float y = circle.y;
    
                if (position == i) {
                    paint.setColor(defaultSelectColor);
                } else {
                    paint.setColor(defaultColor);
                }
                canvas.drawCircle(x, y, radius, paint);
            }
        }
    五、指示器和ViewPager进行关联

    经过以上四个步骤就可以定义出一个指示器了,但是它还是零散的,不能和ViewPager进行联动。如何做到联动?这里让定义的当前类CircleIndicatorView实现ViewPager.OnPageChangeListene接口。然后在setSelectPosition方法里面设置选中的位置,接着进行重绘,这样就会重新调用onDraw方法,在onDraw可以动态设置当前位置选中时候的颜色以及其他位置的颜色。

        @Override
        public void onPageSelected(int position) {
            setSelectPosition(position);
        }
        public void setSelectPosition(int selectPosition) {
            this.position = selectPosition;
            invalidate();
        }
    还有最后一步就是对ViewPager进行初始化的操作:

        public void setUpWithViewPager(ViewPager viewPager) {
            if (viewPager == null) {
                return;
            }
            viewPager.removeOnPageChangeListener(this);
            viewPager.addOnPageChangeListener(this);
            int count = viewPager.getAdapter().getCount();
            setCount(count);
        }
        public void setCount(int count) {
            this.count = count;
        }

    这样指示器Indictor就和ViewPager进行了完全的绑定。


    六、其他属性设置

    1、XML中属性配置

    app:indicatorSelectColor="#f00";//设置选中颜色
    app:indicatorColor="#fff" ; //设置默认颜色
    app:indicatorDistance="8dp";//设置小圆点间间距
    app:indicatorRadius="6dp" ; //设置小圆点半径

    2、代码中属性配置

    在刚刚开始时候自定义了一些属性,他可以直接写在XML布局文件中,这里为了灵活期间,在设定一些方法,用于在代码里面进行控制圆形指示器的属性,如下:

        /**
         * 设置小圆点数量
         * @param count
         */
        public void setCount(int count) {
            this.count = count;
        }
    
        /**
         * 设置选中指示器的颜色
         * @param selectColor
         */
        public void setSelectColor(int selectColor) {
            this.defaultSelectColor = selectColor;
        }
    
        /**
         * 设置指示器默认颜色
         * @param defaultColor
         */
        public void setDefaultColor(int defaultColor) {
            this.defaultColor = defaultColor;
        }
    
        /**
         * 设置选中的位置
         * @param selectPosition
         */
        public void setSelectPosition(int selectPosition) {
            this.position = selectPosition;
            invalidate();
        }
    
        /**
         * 设置Indicator 半径
         * @param radius
         */
        public void setRadius(int radius) {
            this.radius = radius;
        }
    
        /**
         * 设置小圆点之间的距离
         * @param distance
         */
        public void setDistance(int distance) {
            this.distance = distance;
        }


    展开全文
  • 圆形指示器UI图 UI分析 内侧一个白色的虚线弧 外侧有两条弧,一个是灰色的实线弧,一个是白色的虚线弧,实线弧尾部有一个小圆点 中间是文字,大小不一致,颜色是白色 加载的时候需要一个加载动画,实线椭圆进度条...

    项目中的自定义控件比较多,今天应该是最后一个了,看下UI效果

    • UI图

    圆形指示器UI图

    UI分析

    1. 内侧一个白色的虚线弧
    2. 外侧有两条弧,一个是灰色的实线弧,一个是白色的虚线弧,实线弧尾部有一个小圆点
    3. 中间是文字,大小不一致,颜色是白色
    4. 加载的时候需要一个加载动画,实线椭圆进度条跟可用额度数字需要同时从小到达变化
    • 效果图

    圆形指示器效果图

    下面根据UI的分析,来分享一下实现的过程

    自定义属性

    • 文字的大小
    • 线的颜色
    • 虚线的间距

    代码

    //定义属性
     <declare-styleable name="CircleIndicatorView">
            <attr name="largeSize" format="dimension"/>
            <attr name="smallSize" format="dimension"/>
            <attr name="grayColor" format="color"/>
            <attr name="whiteColor" format="color"/>
            <attr name="lineSpace" format="dimension"/>
        </declare-styleable>
    
    //获取属性
    
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleIndicatorView);
            mLargeSize = (int) ta.getDimension(R.styleable.CircleIndicatorView_largeSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 15, context.getResources().getDisplayMetrics()));
            mSmallSize = (int) ta.getDimension(R.styleable.CircleIndicatorView_smallSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 13, context.getResources().getDisplayMetrics()));
            mGrayColor = ta.getColor(R.styleable.CircleIndicatorView_grayColor, Color.GRAY);
            mWhiteColor = ta.getColor(R.styleable.CircleIndicatorView_whiteColor, Color.WHITE);
            ta.recycle();复制代码

    onMeasure

     @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
        }
    
            private int measureWidth(int widthMeasureSpec) {
            int result = 0;
            int specMode = MeasureSpec.getMode(widthMeasureSpec);
            int specSize = MeasureSpec.getSize(widthMeasureSpec);
            switch (specMode) {
                case MeasureSpec.EXACTLY:
                    result = specSize;
                    break;
                case MeasureSpec.UNSPECIFIED:
                case MeasureSpec.AT_MOST:
                    break;
            }
            return result;
        }复制代码

    这里其实不需要考虑MeasureSpec.AT_MOST,因为这个圆的半径是一般是固定的,所以没有处理MeasureSpec.AT_MOST,如果需求需要处理的话其实也很简单,跟自定义View之IndexView进度条(一)中的一样,把相应的宽高进行累加即可,不是分析的重点,所以一笔带过。

    onDraw

    1. 绘制内侧白色虚线弧度
    //不加的话在高版本上虚线不显示
    setLayerType(View.LAYER_TYPE_SOFTWARE,dashPaint);
    //设置虚线间隔
    PathEffect effects = new DashPathEffect(new float[]{20, 6}, 0);
            dashPaint.setPathEffect(effects);
             RectF dashedRectF = new RectF(mCenter - mRadius + 20 + getPaddingLeft(), mCenter - mRadius + 20 + getPaddingTop(), mCenter + mRadius - 20 - getPaddingRight(), mCenter + mRadius - 20 - getPaddingBottom());
            float startAngle = 150;
            canvas.drawArc(dashedRectF, startAngle, sweepAngle, false, dashPaint);复制代码
    1. 绘制外侧灰色弧度
            canvas.drawArc(rectF, startAngle, sweepAngle, false, outPaint);复制代码
    1. 绘制外侧进度弧度
     canvas.drawArc(rectF, startAngle, getInSweepAngle(), false, inPaint);复制代码

    4.绘制外侧进度弧度的小圆点,实际上是一个Bitmap,注释比较详细,也比较简单,就是画布的平移跟旋转这个需要好好理解一下

      //绘制发光的小圆点
            Paint paintCircle = new Paint();
            paintCircle.setStyle(Paint.Style.FILL);
            paintCircle.setAntiAlias(true);//抗锯齿功能
            canvas.translate(getWidth() / 2, getHeight() / 2);//画布平移到圆心
            canvas.rotate(getInSweepAngle() + 60);//旋转画布使得画布的Y轴经过小圆点
            canvas.translate(0, getHeight() / 2 - getPaddingLeft());//再次平移画布至小圆点绘制的位置
           //此处省略了Bitmap的处理过程bmp
            Bitmap dotBitmap = Bitmap.createBitmap(bmp, 0, 0, width, height, matrix, true);
            canvas.drawBitmap(dotBitmap, -15, -15, paintCircle);
            canvas.rotate(-(getInSweepAngle() + 60));//恢复canvas复制代码

    5.绘制文字
    这个在绘制数字的时候需要注意一下,因为传过来的是一个浮点数,需要拆分成整数跟小数两部分,但是当你讲float转化为String然后切割的时候,注意下需要将小数点进行转义,不然会分割失败

    DecimalFormat decimalFormat = new DecimalFormat(".00");//构造方法的字符格式这里如果小数不足2位,会以0补足.
            String value = decimalFormat.format(indexValue);//format 返回的是字符串
            Log.d("value---->", value);
            String[] split = value.split("\\.");
            String text = split[0];//整数部分复制代码

    刚进来时候的加载动画

    这个其实就是两个属性动画同时播放而已,通过计算扇形扫过的角度与数字增长的幅度
    ,然后在属性动画的update里面进行重新绘制整个View,就可以搞定

           float inSweepAngle = sweepAngle * value / 100;
            ValueAnimator angleAnim = ValueAnimator.ofFloat(0f, inSweepAngle);//角度的ValueAnimator
            float inValue = value * 8888 / 100;
            ValueAnimator valueAnim = ValueAnimator.ofFloat(0, inValue);//数字变化的ValueAnimator
            //一起播放动画
             animatorSet.playTogether(angleAnim, valueAnim);复制代码

    提供一个方法,供外部调用

     public void goToPoint(float value) {
           //在方法中进行播放动画
        }复制代码

    使用方法

    mCircleIndicatorView.goToPoint(value);

    Demo下载地址

    展开全文
  • 很多ViewPager和下面的图形绑定一块来滚动,也就是指示器指示器可以是很多,正方形,长方形,三角形,圆形等。这里举一个三角形例子:效果图:未滑动 滑动中:这里滑动只要超过倒数第二个就自动向后滑动。上代码...
  • 那么今天就通过自定义View来创建一个属于自己的指示器。 效果图: 首先,在res - values下新建一个attr文件, 可命名为:attr_indicator.xml 代码如下: <resources> <declare-styleable name=...
  • 自定义Android圆形指示器
  • Android 自定义控件之圆形页面指示器CirclePageIndicator带划动效果前言感谢效果图目标流程自定义属性自定义默认属性自定义接口创建控件类继承View声明属性变量初始化属性信息测量布局绘制布局记录变化信息复写...
  • Android自定义View之超简单圆形数字指示器
  • 上一次说了自定义圆形页面指示器 Android 自定义控件之圆形页面指示器CirclePageIndicator带划动效果 应该很少人耐心看完了。。。 这一次就把它打包成依赖库,直接添加依赖使用就好了。 不过为了方便我自己的项目...
  • ViewPager指示器实现代码如下,我主要是实现了圆形指示器。 首先在attrs文件中添加: <declare-styleable name="ViewPagerIndicator"> <attr name="unSelectCirclerColor" format="color"></...
  • LKAWaveCircleProgressBar 是一款带有双波浪动画的圆形进度指示器视图,可自定义圆形容器的边框颜色、边框线宽,双波浪的颜色,动画时间,进度改变时间。
  • Banner是我们经常会遇到的一个功能,有很多是需要支持滑动是有个当前的指示器界面,一般如果为了图省事的方案可以用LinearLayout然后不断的在里面添加View,把背景通过shape来设置圆形。这种做法虽然比较简单,但是...

空空如也

空空如也

1 2 3 4 5
收藏数 82
精华内容 32
关键字:

自定义圆形指示器