這次要說的指示器可能和大家常見的不同,這個是純繪製出來的,即只 onDraw出來的。也許不夠完善,歡迎大家提出問題。
github:https://github.com/ai2101039/YLPagerIndicator
有人說,這個github 有輪子,可是光使用輪子只是一個搬運工啊,而且github的輪子是爲了適應各種情況,app 代碼量可增加不少;
有人說,你這個指示器,我們可以用select xml,可是 xml的佔用內存啊,而且也不靈活啊。
能學習到什麼?小編就當各位都會,那麼就溫習一下。
1、自定義View 屬性
2、onMeasure 中,wrap_content 設置最小值
3、繪製圓環 和 實心圓
重點:
圓環的直徑計算 = 空心圓直徑 + 線寬
比如:空心圓半徑 10,圓環線寬 2,則 圓環直徑爲 22。而空心圓直徑爲 18
如圖
也就是說 大家在 onDraw裏面,
空心圓,
半徑爲10,線寬爲2,實際空心圓半徑 11,實際空白區9
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;
/**
* @author 高延榮
* @date 2018/12/11 16:48
* 描述: ViewPager 指示器
*/
public class YLPagerIndicator extends View {
private Context mContext;
private ViewPager mViewPager;
private PagerAdapter mAdapter;
/**
* 指示器數量
*/
private int count;
/**
* 用於繪製圓環的畫筆
*/
private Paint paintArc;
/**
* 用於繪製是新的畫筆
*/
private Paint paintFill;
/**
* 圓環直徑
* 重點:圓環的直徑 = 2 * radiusArc + stockWidthArc
* 即:空心圓直徑 + 線寬,而不是 空心圓直徑 + 2 * 線寬
*/
private int diaArc;
/**
* 圓環中空心圓的半徑
*/
private float radiusArc;
/**
* 圓環中線寬
*/
private float stockWidthArc;
/**
* 圓環顏色
*/
private int colorArc;
/**
* 實心半徑
*/
private float radiusFill;
/**
* 實心顏色
*/
private int colorFill;
/**
* 圓環間距
*/
private float space;
/**
* 選中的position
*/
private int selectPosition;
public YLPagerIndicator(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mContext = context;
// 圓環畫筆/空心/抗鋸齒
paintArc = new Paint();
paintArc.setStyle(Paint.Style.STROKE);
paintArc.setAntiAlias(true);
// 實心畫筆/抗鋸齒
paintFill = new Paint();
paintFill.setAntiAlias(true);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.YLPagerIndicator);
radiusArc = typedArray.getDimension(R.styleable.YLPagerIndicator_radiusArc, 6);
stockWidthArc = typedArray.getDimension(R.styleable.YLPagerIndicator_stockWidthArc, 1);
colorArc = typedArray.getColor(R.styleable.YLPagerIndicator_colorArc, Color.WHITE);
radiusFill = typedArray.getDimension(R.styleable.YLPagerIndicator_radiusFill, 3);
colorFill = typedArray.getColor(R.styleable.YLPagerIndicator_colorFill, Color.WHITE);
count = typedArray.getInteger(R.styleable.YLPagerIndicator_count, 1);
space = typedArray.getDimension(R.styleable.YLPagerIndicator_radiusFill, 10);
typedArray.recycle();
// 使用工具函數,將dp 轉爲 px
translateSize(radiusArc, stockWidthArc, radiusFill, space);
// 使用工具函數,給畫筆設置屬性
setPaint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize;
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize;
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
// 圓環直徑,這裏是個重點,一定是 2 * 內圓的半徑 + 線寬,而不是 2 * 內圓的半徑 + 2 * 線寬
diaArc = (int) (2 * radiusArc + stockWidthArc);
// 指示器總寬度
int contentWidth = count * diaArc + (int) ((count - 1) * space);
// 如果是 wrap_content 則設置最小寬高
if (widthMode == MeasureSpec.AT_MOST) {
widthSize = contentWidth + paddingLeft + paddingRight;
widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
}
if (heightMode == MeasureSpec.AT_MOST) {
heightSize = diaArc + paddingTop + paddingBottom;
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
}
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
/**
* 設置圓環/實心圓
*
* @param radiusArc 圓環半徑
* @param widthArc 圓環寬度
* @param colorArc 圓環顏色
* @param radiusFill 實心半徑
* @param colorFill 實心顏色
* @param space 圓環間距
* @param requestLayout 重新測量還是重新繪製,首先,重繪是肯定的。
* 關於重新測量,比如 一開始使用的默認,那麼整體佈局高度是 11dp,
* 如果你那邊想要提高或者縮小這個高度,又使用的是 wrap_content,
* 那麼久需要進行重新測量
*/
public void setIndicator(float radiusArc, float widthArc, @ColorInt int colorArc,
float radiusFill, @ColorInt int colorFill,
float space, boolean requestLayout) {
translateSize(radiusArc, widthArc, radiusFill, space);
this.colorArc = colorArc;
this.colorFill = colorFill;
setPaint();
// 重繪
if (requestLayout) {
requestLayout();
} else {
invalidate();
}
}
/**
* 設置ViewPager
*
* @param viewPager
* @param requestLayout 是否需要重新onMeasure,
* 比如,你未設置指示器個數,那麼默認就是 1個,
* 這時候設置ViewPager,也只是顯示1個指示器
* 所以可以通過 requestLayout() 來重新 onMeasure
*/
public void setViewPager(ViewPager viewPager, boolean requestLayout) {
mViewPager = viewPager;
mAdapter = viewPager.getAdapter();
mViewPager.addOnPageChangeListener(mPagerListener);
count = mAdapter.getCount();
if (requestLayout) {
requestLayout();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 先繪製圓環
for (int i = 0; i < count; i++) {
float cx = (i + 0.5F) * diaArc + getPaddingLeft() + i * space;
float cy = diaArc / 2 + getPaddingTop();
canvas.drawCircle(cx, cy, radiusArc, paintArc);
}
// 繪製實心圓
float cx = (selectPosition + 0.5F) * diaArc + getPaddingLeft() + selectPosition * space;
float cy = diaArc / 2 + getPaddingTop();
canvas.drawCircle(cx, cy, radiusFill, paintFill);
}
private ViewPager.OnPageChangeListener mPagerListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
selectPosition = position;
invalidate();
}
@Override
public void onPageScrollStateChanged(int state) {
}
};
/////////////////////////////// 工具函數 ///////////////////////////////
/**
* 將dp 轉換爲 px
*
* @param radiusArc 圓環內 空心圓半徑
* @param widthArc 圓環線寬
* @param radiusFill 實心圓半徑
* @param space 圓環間距
*/
private void translateSize(float radiusArc, float widthArc, float radiusFill, float space) {
this.radiusArc = YLUtil.dp2px(mContext, radiusArc);
this.stockWidthArc = YLUtil.dp2px(mContext, widthArc);
this.radiusFill = YLUtil.dp2px(mContext, radiusFill);
this.space = YLUtil.dp2px(mContext, space);
}
/**
* 畫筆設置屬性
*/
private void setPaint() {
paintArc.setColor(colorArc);
paintArc.setStrokeWidth(stockWidthArc);
paintFill.setColor(colorFill);
}
}