import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.View;
import com.example.googleplay.utils.UIUtils;
public class IndicatorView extends View {
private static final int POSITION_NONE = -1;
private Drawable mDrbIndicator;
private int mCount;
private int mSelection;
private int mInterval;
public IndicatorView(Context context) {
super(context);
init();
}
/** 初始化 */
private void init() {
mSelection = POSITION_NONE;
}
/** 设置数目 */
public void setCount(int count) {
count = count > 0 ? count : 0;
if (count != mCount) {
mCount = count;
requestLayoutInner();
requestInvalidate();
}
}
/** 设置选中项 */
public void setSelection(int selection) {
if (selection != mSelection) {
mSelection = selection;
requestInvalidate();
}
}
/** 设置选中项的图片 */
public void setIndicatorDrawable(Drawable drawable) {
mDrbIndicator = drawable;
requestLayoutInner();
requestInvalidate();
}
/** 设置item之间间隔 */
public void setInterval(int interval) {
if (interval != mInterval) {
mInterval = interval;
requestLayoutInner();
requestInvalidate();
}
}
/**
* invalidate:View本身调用迫使view重画。
*/
private void requestInvalidate() {
if (UIUtils.isRunInMainThread()) {
invalidate();
} else {
postInvalidate();
}
}
/**
* requestLayout:当view确定自身已经不再适合现有的区域时, 该view本身调用这个方法要求parent
* view重新调用他的onMeasure onLayout来对重新设置自己位置。
* 特别的当view的layoutparameter发生改变,并且它的值还没能应用到view上,这时候适合调用这个方法。
*/
private void requestLayoutInner() {
UIUtils.runInMainThread(new Runnable() {
@Override
public void run() {
requestLayout();
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width;
int height;
// 而是将模式和尺寸组合在一起的数值
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 计算高度
/*
* MeasureSpec.EXACTLY是精确尺寸,当我们将控件的
* layout_width或layout_height指定为具体数值时如andorid:layout_width="50dip",
* 或者为FILL_PARENT是,都是控件大小已经确定的情况,都是精确尺寸。
*/
if (widthMode == MeasureSpec.EXACTLY) {// 如果是精确的,就采用精确值
width = widthSize;
} else {// 否则就采用图片的宽度 获取资源文件中图片的大小,最简单的最直接的方法
int indicatorW = mDrbIndicator == null ? 0 : mDrbIndicator.getIntrinsicWidth();
int expectedW = indicatorW * mCount + mInterval * (mCount - 1) + getPaddingLeft()
+ getPaddingRight();
if (widthMode == MeasureSpec.AT_MOST) {
/**
* MeasureSpec.AT_MOST是最大尺寸,当控件的layout_width或layout_height指定为
* WRAP_CONTENT时,控件大小一般随着控件的子空间或内容进行变化,此时控件尺寸只要不超过
* 父控件允许的最大尺寸即可。因此,此时的mode是AT_MOST,size给出了父控件允许的最大尺寸。
*/
width = Math.min(expectedW, widthSize);
} else {
width = expectedW;
}
}
// 计算高度
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {// 否则就采用图片的高度
int indicatorH = mDrbIndicator == null ? 0 : mDrbIndicator.getIntrinsicHeight();
int expectedH = indicatorH + getPaddingTop() + getPaddingBottom();
if (expectedH == MeasureSpec.AT_MOST) {
height = Math.min(expectedH, heightSize);
} else {
height = expectedH;
}
}
// 继承View,实现自己想要的组件,那么需要使用到setMeasuredDimension这个方法,这个方法决定了当前View的大小
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrbIndicator == null || mCount == 0) {
return;
}
int w = mDrbIndicator.getIntrinsicWidth();
int h = mDrbIndicator.getIntrinsicHeight();
int horizontalSideSpacing = (getWidth() - getPaddingLeft() - w * mCount - mInterval
* (mCount - 1)) / 2;
int verticalSideSpacing = (getHeight() - getPaddingTop() - getPaddingBottom() - h) / 2;
int l = getPaddingLeft() + horizontalSideSpacing;
int t = getPaddingTop() + verticalSideSpacing;
int rEdge = getRight() - getPaddingRight();
int bEdge = getBottom() - getPaddingBottom();
// 计算出间隙和范围,然后画图片,实际画的是同一张图片,只是改变图片的bounds
for (int i = 0; i < mCount; i++) {
mDrbIndicator.setBounds(l, t, Math.min(1 + w, rEdge), Math.min(t + h, bEdge));
if (i == mSelection) {
mDrbIndicator.setState(new int[] {
android.R.attr.state_selected
});
} else {
mDrbIndicator.setState(null);
}
mDrbIndicator.draw(canvas);
l += w + mInterval;
}
}
}