前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android打造不一样的新手引导页面(一)

Android打造不一样的新手引导页面(一)

作者头像
程序员徐公
发布2018-09-18 16:52:31
6010
发布2018-09-18 16:52:31
举报

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1341874

Android打造不一样的新手引导页面(一)


关于页面导航器的,可以查看我的这一篇博客仿网易新闻的顶部导航指示器

本篇博客主要讲解怎样自定义一个circleIndicator控件?

下一遍博客主要讲解怎样更改ViewPager切换的效果, 预计明天晚上之前更新。

效果图如下

1)首先我们先来看一下要怎样使用我们的circleIndicator控件

其实很简单,值需要两个步骤

1) 在xml布局文件里面

代码语言:javascript
复制
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <android.support.v4.view.ViewPager
        android:layout_below="@id/rl_header"
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"

        android:layout_marginTop="20dp">

    </android.support.v4.view.ViewPager>

    <com.xujun.administrator.customviewspecif.view.CirclePageIndicator
        android:id="@+id/circle_indicator"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="20dp">

    </com.xujun.administrator.customviewspecif.view.CirclePageIndicator>
</RelativeLayout>

2)在代码里面

代码语言:javascript
复制
mViewPager = (ViewPager) findViewById(R.id.viewPager);
mCirclePageIndicator = (CirclePageIndicator) findViewById(R.id.circle_indicator);

//注意下面初始化的顺序不可以调换
mFragemntAdapter = new BaseFragemntAdapter(
        getSupportFragmentManager(), mFragments);
mViewPager.setAdapter(mFragemntAdapter);

//将mCirclePageIndicator与我们的mViewPager绑定在一起
mCirclePageIndicator.setViewPager(mViewPager);

扩展

1)在xml布局里面更改我们的样式

代码语言:javascript
复制
xmlns:app="http://schemas.android.com/apk/res-auto"
//例如更改我们移动小圆点的颜色
app:fillColor="#fff"


//其他属性的更改请参考以下我们自定义的属性

<declare-styleable name="CirclePageIndicator">
    <!-- Whether or not the indicators should be centered. -->
    <attr name="centered" />
    <!-- Color of the filled circle that represents the current page. -->
    <attr name="fillColor" format="color" />
    <!-- Color of the filled circles that represents pages. -->
    <attr name="pageColor" format="color" />
    <!-- Orientation of the indicator. -->
    <attr name="android:orientation"/>
    <!-- Radius of the circles. This is also the spacing between circles. -->
    <attr name="radius" format="dimension" />
    <!-- Whether or not the selected indicator snaps to the circles. -->
    <attr name="snap" format="boolean" />
    <!-- Color of the open circles. -->
    <attr name="strokeColor" format="color" />
    <!-- Width of the stroke used to draw the circles. -->
    <attr name="strokeWidth" />
    <!-- View background -->
    <attr name="android:background"/>
</declare-styleable>

2)在Java代码里面动态更改

代码语言:javascript
复制
// 设置滑动的时候移动的小圆点是否跳跃
mCirclePageIndicator.setSnap(false);
//设置小圆点的半径
mCirclePageIndicator.setRadius(10 * density);
// 设置页面小圆点的颜色
mCirclePageIndicator.setPageColor(0x880000FF);
// 设置移动的小圆点的颜色
mCirclePageIndicator.setFillColor(0xFF888888);
// 设置外边框的颜色
mCirclePageIndicator.setStrokeColor(0xFF000000);
//设置外表框的宽度
mCirclePageIndicator.setStrokeWidth(2 * density);

2)下面我们一起来看我们是怎样CircleIndicator是怎样实现的

大概可以分为以下几个步骤

  • (1)继承View,在构造方法里面做一些初始化工作,包括初始化我们的自定义属性及画笔等
代码语言:javascript
复制
public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    if (isInEditMode()) return;

    //初始化自定义属性
    final Resources res = getResources();

    final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color);
    final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);
    final int defaultOrientation = res.getInteger(R.integer
            .default_circle_indicator_orientation);

  在这里省略了若干方法

    a.recycle();

    final ViewConfiguration configuration = ViewConfiguration.get(context);
    mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
}
  • (2) 在我们的onMeasure方法里面根据方向的不同测量我们的大小
代码语言:javascript
复制
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == HORIZONTAL) {
        setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec));
    } else {
        setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec));
    }
}


/**
 * Determines the width of this view
 *
 * @param measureSpec A measureSpec packed into an int
 * @return The width of the view, honoring constraints from measureSpec
 */
private int measureLong(int measureSpec) {
    int result;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) {
        //We were told how big to be
        result = specSize;
    } else {
        //Calculate the width according the views count
        final int count = mViewPager.getAdapter().getCount();
        result = (int) (getPaddingLeft() + getPaddingRight()
                + (count * 2 * mRadius) + (count - 1) * mRadius + 1);
        //Respect AT_MOST value if that was what is called for by measureSpec
        if (specMode == MeasureSpec.AT_MOST) {
            result = Math.min(result, specSize);
        }
    }
    return result;
}

/**
 * Determines the height of this view
 *
 * @param measureSpec A measureSpec packed into an int
 * @return The height of the view, honoring constraints from measureSpec
 */
private int measureShort(int measureSpec) {
    int result;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    if (specMode == MeasureSpec.EXACTLY) {
        //We were told how big to be
        result = specSize;
    } else {
        //Measure the height
        result = (int) (2 * mRadius + getPaddingTop() + getPaddingBottom() + 1);
        //Respect AT_MOST value if that was what is called for by measureSpec
        if (specMode == MeasureSpec.AT_MOST) {
            result = Math.min(result, specSize);
        }
    }
    return result;
}
  • (3)提供一个setViewPager(ViewPager view)方法将我们的CirclePageIndicator 绑定在一起
代码语言:javascript
复制
@Override
public void setViewPager(ViewPager view) {
    if (mViewPager == view) {
        return;
    }
    if (mViewPager != null) {
        mViewPager.addOnPageChangeListener(null);
    }
    if (view.getAdapter() == null) {
        throw new IllegalStateException("ViewPager does not have adapter instance.");
    }
    mViewPager = view;
    mViewPager.addOnPageChangeListener(this);
    invalidate();
}

里面主要的逻辑简单来说就是判断我们的ViewPager是否已经设置adapter,没有的话抛出异常,接着坚挺ViewPager的PageChangListener事件。

调用invalidate()方法重新绘制CirclePagerIndicator

  • (4)在滑动ViewPager的 时候拿到相应的偏移量
代码语言:javascript
复制
@Override
public void onPageScrollStateChanged(int state) {
    mScrollState = state;

    if (mListener != null) {
        mListener.onPageScrollStateChanged(state);
    }
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    mCurrentPage = position;
    mPageOffset = positionOffset;
    invalidate();

    if (mListener != null) {
        mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
    }
}

@Override
public void onPageSelected(int position) {
    if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) {
        mCurrentPage = position;
        mSnapPage = position;
        invalidate();
    }

    if (mListener != null) {
        mListener.onPageSelected(position);
    }
}
  • (5)接着在onDraw()方法里面根据偏移量绘制我们的小圆点
代码语言:javascript
复制
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if (mViewPager == null) {
        return;
    }
    final int count = mViewPager.getAdapter().getCount();
    if (count == 0) {
        return;
    }

    if (mCurrentPage >= count) {
        setCurrentItem(count - 1);
        return;
    }

    int longSize;
    int longPaddingBefore;
    int longPaddingAfter;
    int shortPaddingBefore;
    //  根据方向的不同初始化各个变量
    if (mOrientation == HORIZONTAL) {
        longSize = getWidth();
        longPaddingBefore = getPaddingLeft();
        longPaddingAfter = getPaddingRight();
        shortPaddingBefore = getPaddingTop();
    } else {
        longSize = getHeight();
        longPaddingBefore = getPaddingTop();
        longPaddingAfter = getPaddingBottom();
        shortPaddingBefore = getPaddingLeft();
    }

    final float threeRadius = mRadius * 3;
    final float shortOffset = shortPaddingBefore + mRadius;
    float longOffset = longPaddingBefore + mRadius;
    /**
     * 居中显示的时候
     */
    if (mCentered) {
        longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f)
                - ((count * threeRadius) / 2.0f);
    }

    float dX;
    float dY;

    float pageFillRadius = mRadius;
    if (mPaintStroke.getStrokeWidth() > 0) {
        pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f;
    }

    //Draw stroked circles
    for (int iLoop = 0; iLoop < count; iLoop++) {
        float drawLong = longOffset + (iLoop * threeRadius);
        if (mOrientation == HORIZONTAL) {
            dX = drawLong;
            dY = shortOffset;
        } else {
            dX = shortOffset;
            dY = drawLong;
        }
        // Only paint fill if not completely transparent
        if (mPaintPageFill.getAlpha() > 0) {
            canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill);
        }

        // Only paint stroke if a stroke width was non-zero
        if (pageFillRadius != mRadius) {
            canvas.drawCircle(dX, dY, mRadius, mPaintStroke);
        }
    }

    //下面绘制移动的实心圆

    float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius;
    //根据移动的实心圆是否跳跃计算偏移量
    if (!mSnap) {
        cx += mPageOffset * threeRadius;
    }
    if (mOrientation == HORIZONTAL) {
        dX = longOffset + cx;
        dY = shortOffset;
    } else {
        dX = shortOffset;
        dY = longOffset + cx;
    }
    canvas.drawCircle(dX, dY, mRadius, mPaintFill);
}

其实核心就是根据方向的不同绘制我们的小圆点,那些偏移量是一些数学运算而已,不过别小看这些,计算这些偏移量还是挺繁琐的。

到此我们的源码分析为止


题外话

如果各位觉得还行的话,欢迎在github上面 star或者 fork,谢谢 ,github项目地址ViewPagerTabIndicator

源码github参考库ViewPagerIndicator:

转载请注明原博客地址:

源码下载地址:

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年08月20日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Android打造不一样的新手引导页面(一)
    • 效果图如下
      • 1)首先我们先来看一下要怎样使用我们的circleIndicator控件
        • 扩展
      • 2)下面我们一起来看我们是怎样CircleIndicator是怎样实现的
        • 题外话
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档