一:
自定义View之onMeasure()
可以说重载onMeasure(),onLayout(),onDraw()三个函数构建了自定义View的外观形象。再加上onTouchEvent()等重载视图的行为,可以构建任何我们需要的可感知到的自定义View。
本节我们探索自定义View中onMeasure()起到了什么样的作用,题外要插的一句是,Activity框架,View框架中大量的on函数基本上都应用到了Template模式,掌握这一模式对于理解这些框架大有裨益。
我们知道,不管是自定义View还是系统提供的TextView这些,它们都必须放置在LinearLayout等一些ViewGroup中,因此理论上我们可以很好的理解onMeasure(),onLayout(),onDraw()这三个函数:1.View本身大小多少,这由onMeasure()决定;2.View在ViewGroup中的位置如何,这由onLayout()决定;3.绘制View,onDraw()定义了如何绘制这个View。
首先我们要理解的是widthMeasureSpec, heightMeasureSpec这两个参数是从哪里来的?onMeasure()函数由包含这个View的具体的ViewGroup调用,因此值也是从这个ViewGroup中传入的。这里我直接给出答案:子类View的这两个参数,由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同决定。权值weight也是尤其需要考虑的因素,有它的存在情况可能会稍微复杂点。
二:
ViewConfiguration.getScaledTouchSlop ()
三:
实现这种不等长圆角PageIndicator
CustomLinePageIndicator
mPaintSelected.setStrokeCap(Paint.Cap.ROUND);//处理圆角
mPaintUnselected.setStrokeCap(Paint.Cap.ROUND);
//处理不等长
for
(
int
i =
0
;
i < count
;
++i) {
float
dx1 = i >
this
.
mCurrentPage
?horizontalOffset + (
float
)i * lineWidthAndGap+
20
:horizontalOffset + (
float
)i * lineWidthAndGap
;
float
dx2 = dx1 + (i ==
this
.
mCurrentPage
?
this
.
mLineWidth
+
20
:
this
.
mLineWidth
)
;
canvas.drawLine(dx1
,
verticalOffset
,
dx2
,
verticalOffset
,
i ==
this
.
mCurrentPage
?
this
.
mPaintSelected
:
this
.
mPaintUnselected
)
;
}
完整代码:
import
android.content.Context
;
import
android.content.res.Resources
;
import
android.content.res.TypedArray
;
import
android.graphics.Canvas
;
import
android.graphics.Paint
;
import
android.graphics.drawable.Drawable
;
import
android.os.Parcel
;
import
android.os.Parcelable
;
import
android.support.v4.view.MotionEventCompat
;
import
android.support.v4.view.PagerAdapter
;
import
android.support.v4.view.ViewConfigurationCompat
;
import
android.support.v4.view.ViewPager
;
import
android.util.AttributeSet
;
import
android.util.FloatMath
;
import
android.view.MotionEvent
;
import
android.view.View
;
import
android.view.ViewConfiguration
;
import
com.viewpagerindicator.LinePageIndicator
;
import
com.viewpagerindicator.PageIndicator
;
/**
* Created by chao on 2017/11/22.
*/
public class
CustomLinePageIndicator
extends
View
implements
PageIndicator {
private static final int
INVALID_POINTER
= -
1
;
private final
Paint
mPaintUnselected
;
private final
Paint
mPaintSelected
;
private
ViewPager
mViewPager
;
private
ViewPager.OnPageChangeListener
mListener
;
private int
mCurrentPage
;
private boolean
mCentered
;
private float
mLineWidth
;
private float
mGapWidth
;
private int
mTouchSlop
;
private float
mLastMotionX
;
private int
mActivePointerId
;
private boolean
mIsDragging
;
public
CustomLinePageIndicator
(Context context) {
this
(context
,
(AttributeSet)
null
)
;
}
public
CustomLinePageIndicator
(Context context
,
AttributeSet attrs) {
this
(context
,
attrs
,
com.viewpagerindicator.R.attr.
vpiLinePageIndicatorStyle
)
;
}
public
CustomLinePageIndicator
(Context context
,
AttributeSet attrs
, int
defStyle) {
super
(context
,
attrs
,
defStyle)
;
this
.
mPaintUnselected
=
new
Paint(
1
)
;
mPaintUnselected
.setStrokeCap(Paint.Cap.
ROUND
)
;
this
.
mPaintSelected
=
new
Paint(
1
)
;
mPaintSelected
.setStrokeCap(Paint.Cap.
ROUND
)
;
this
.
mLastMotionX
= -
1.0F
;
this
.
mActivePointerId
= -
1
;
if
(!
this
.isInEditMode()) {
Resources res =
this
.getResources()
;
int
defaultSelectedColor = res.getColor(com.viewpagerindicator.R.color.
default_line_indicator_selected_color
)
;
int
defaultUnselectedColor = res.getColor(com.viewpagerindicator.R.color.
default_line_indicator_unselected_color
)
;
float
defaultLineWidth = res.getDimension(com.viewpagerindicator.R.dimen.
default_line_indicator_line_width
)-
5
;
float
defaultGapWidth = res.getDimension(com.viewpagerindicator.R.dimen.
default_line_indicator_gap_width
)+
20
;
float
defaultStrokeWidth = res.getDimension(com.viewpagerindicator.R.dimen.
default_line_indicator_stroke_width
)
;
boolean
defaultCentered = res.getBoolean(com.viewpagerindicator.R.bool.
default_line_indicator_centered
)
;
TypedArray a = context.obtainStyledAttributes(attrs
,
com.viewpagerindicator.R.styleable.
LinePageIndicator
,
defStyle
,
0
)
;
this
.
mCentered
= a.getBoolean(
1
,
defaultCentered)
;
this
.
mLineWidth
= a.getDimension(
5
,
defaultLineWidth)
;
this
.
mGapWidth
= a.getDimension(
6
,
defaultGapWidth)
;
this
.setStrokeWidth(a.getDimension(
3
,
defaultStrokeWidth+
5
))
;
this
.
mPaintUnselected
.setColor(a.getColor(
4
,
defaultUnselectedColor))
;
this
.
mPaintSelected
.setColor(a.getColor(
2
,
defaultSelectedColor))
;
Drawable background = a.getDrawable(
0
)
;
if
(background !=
null
) {
this
.setBackgroundDrawable(background)
;
}
a.recycle()
;
ViewConfiguration configuration = ViewConfiguration.
get
(context)
;
this
.
mTouchSlop
= ViewConfigurationCompat.
getScaledPagingTouchSlop
(configuration)
;
}
}
public void
setCentered
(
boolean
centered) {
this
.
mCentered
= centered
;
this
.invalidate()
;
}
public boolean
isCentered
() {
return this
.
mCentered
;
}
public void
setUnselectedColor
(
int
unselectedColor) {
this
.
mPaintUnselected
.setColor(unselectedColor)
;
this
.invalidate()
;
}
public int
getUnselectedColor
() {
return this
.
mPaintUnselected
.getColor()
;
}
public void
setSelectedColor
(
int
selectedColor) {
this
.
mPaintSelected
.setColor(selectedColor)
;
this
.invalidate()
;
}
public int
getSelectedColor
() {
return this
.
mPaintSelected
.getColor()
;
}
public void
setLineWidth
(
float
lineWidth) {
this
.
mLineWidth
= lineWidth
;
this
.invalidate()
;
}
public float
getLineWidth
() {
return this
.
mLineWidth
;
}
public void
setStrokeWidth
(
float
lineHeight) {
this
.
mPaintSelected
.setStrokeWidth(lineHeight)
;
this
.
mPaintUnselected
.setStrokeWidth(lineHeight)
;
this
.invalidate()
;
}
public float
getStrokeWidth
() {
return this
.
mPaintSelected
.getStrokeWidth()
;
}
public void
setGapWidth
(
float
gapWidth) {
this
.
mGapWidth
= gapWidth
;
this
.invalidate()
;
}
public float
getGapWidth
() {
return this
.
mGapWidth
;
}
protected void
onDraw
(Canvas canvas) {
super
.onDraw(canvas)
;
if
(
this
.
mViewPager
!=
null
) {
int
count =
this
.
mViewPager
.getAdapter().getCount()
;
if
(count !=
0
) {
if
(
this
.
mCurrentPage
>= count) {
this
.setCurrentItem(count -
1
)
;
}
else
{
float
lineWidthAndGap =
this
.
mLineWidth
+
this
.
mGapWidth
;
float
indicatorWidth = (
float
)count * lineWidthAndGap -
this
.
mGapWidth
;
float
paddingTop = (
float
)
this
.getPaddingTop()
;
float
paddingLeft = (
float
)
this
.getPaddingLeft()
;
float
paddingRight = (
float
)
this
.getPaddingRight()
;
float
verticalOffset = paddingTop + ((
float
)
this
.getHeight() - paddingTop - (
float
)
this
.getPaddingBottom()) /
2.0F
;
float
horizontalOffset = paddingLeft
;
if
(
this
.
mCentered
) {
horizontalOffset = paddingLeft + (((
float
)
this
.getWidth() - paddingLeft - paddingRight) /
2.0F
- indicatorWidth /
2.0F
)
;
}
for
(
int
i =
0
;
i < count
;
++i) {
float
dx1 = i >
this
.
mCurrentPage
?horizontalOffset + (
float
)i * lineWidthAndGap+
15
:horizontalOffset + (
float
)i * lineWidthAndGap
;
float
dx2 = dx1 + (i ==
this
.
mCurrentPage
?
this
.
mLineWidth
+
15
:
this
.
mLineWidth
)
;
canvas.drawLine(dx1
,
verticalOffset
,
dx2
,
verticalOffset
,
i ==
this
.
mCurrentPage
?
this
.
mPaintSelected
:
this
.
mPaintUnselected
)
;
}
}
}
}
}
public boolean
onTouchEvent
(MotionEvent ev) {
if
(
super
.onTouchEvent(ev)) {
return true;
}
else if
(
this
.
mViewPager
!=
null
&&
this
.
mViewPager
.getAdapter().getCount() !=
0
) {
int
action = ev.getAction() &
255
;
int
count
;
int
width
;
float
deltaX
;
switch
(action) {
case
0
:
this
.
mActivePointerId
= MotionEventCompat.
getPointerId
(ev
,
0
)
;
this
.
mLastMotionX
= ev.getX()
;
break;
case
1
:
case
3
:
if
(!
this
.
mIsDragging
) {
count =
this
.
mViewPager
.getAdapter().getCount()
;
width =
this
.getWidth()
;
deltaX = (
float
)width /
2.0F
;
float
sixthWidth = (
float
)width /
6.0F
;
if
(
this
.
mCurrentPage
>
0
&& ev.getX() < deltaX - sixthWidth) {
if
(action !=
3
) {
this
.
mViewPager
.setCurrentItem(
this
.
mCurrentPage
-
1
)
;
}
return true;
}
if
(
this
.
mCurrentPage
< count -
1
&& ev.getX() > deltaX + sixthWidth) {
if
(action !=
3
) {
this
.
mViewPager
.setCurrentItem(
this
.
mCurrentPage
+
1
)
;
}
return true;
}
}
this
.
mIsDragging
=
false;
this
.
mActivePointerId
= -
1
;
if
(
this
.
mViewPager
.isFakeDragging()) {
this
.
mViewPager
.endFakeDrag()
;
}
break;
case
2
:
count = MotionEventCompat.
findPointerIndex
(ev
, this
.
mActivePointerId
)
;
float
x = MotionEventCompat.
getX
(ev
,
count)
;
deltaX = x -
this
.
mLastMotionX
;
if
(!
this
.
mIsDragging
&& Math.
abs
(deltaX) > (
float
)
this
.
mTouchSlop
) {
this
.
mIsDragging
=
true;
}
if
(
this
.
mIsDragging
) {
this
.
mLastMotionX
= x
;
if
(
this
.
mViewPager
.isFakeDragging() ||
this
.
mViewPager
.beginFakeDrag()) {
this
.
mViewPager
.fakeDragBy(deltaX)
;
}
}
case
4
:
default
:
break;
case
5
:
count = MotionEventCompat.
getActionIndex
(ev)
;
this
.
mLastMotionX
= MotionEventCompat.
getX
(ev
,
count)
;
this
.
mActivePointerId
= MotionEventCompat.
getPointerId
(ev
,
count)
;
break;
case
6
:
count = MotionEventCompat.
getActionIndex
(ev)
;
width = MotionEventCompat.
getPointerId
(ev
,
count)
;
if
(width ==
this
.
mActivePointerId
) {
int
newPointerIndex = count ==
0
?
1
:
0
;
this
.
mActivePointerId
= MotionEventCompat.
getPointerId
(ev
,
newPointerIndex)
;
}
this
.
mLastMotionX
= MotionEventCompat.
getX
(ev
,
MotionEventCompat.
findPointerIndex
(ev
, this
.
mActivePointerId
))
;
}
return true;
}
else
{
return false;
}
}
public void
setViewPager
(ViewPager viewPager) {
if
(
this
.
mViewPager
!= viewPager) {
if
(
this
.
mViewPager
!=
null
) {
this
.
mViewPager
.setOnPageChangeListener((ViewPager.OnPageChangeListener)
null
)
;
}
if
(viewPager.getAdapter() ==
null
) {
throw new
IllegalStateException(
"ViewPager does not have adapter instance."
)
;
}
else
{
this
.
mViewPager
= viewPager
;
this
.
mViewPager
.setOnPageChangeListener(
this
)
;
this
.invalidate()
;
}
}
}
public void
setViewPager
(ViewPager view
, int
initialPosition) {
this
.setViewPager(view)
;
this
.setCurrentItem(initialPosition)
;
}
public void
setCurrentItem
(
int
item) {
if
(
this
.
mViewPager
==
null
) {
throw new
IllegalStateException(
"ViewPager has not been bound."
)
;
}
else
{
this
.
mViewPager
.setCurrentItem(item)
;
this
.
mCurrentPage
= item
;
this
.invalidate()
;
}
}
public void
notifyDataSetChanged
() {
this
.invalidate()
;
}
public void
onPageScrollStateChanged
(
int
state) {
if
(
this
.
mListener
!=
null
) {
this
.
mListener
.onPageScrollStateChanged(state)
;
}
}
public void
onPageScrolled
(
int
position
, float
positionOffset
, int
positionOffsetPixels) {
if
(
this
.
mListener
!=
null
) {
this
.
mListener
.onPageScrolled(position
,
positionOffset
,
positionOffsetPixels)
;
}
}
public void
onPageSelected
(
int
position) {
this
.
mCurrentPage
= position
;
this
.invalidate()
;
if
(
this
.
mListener
!=
null
) {
this
.
mListener
.onPageSelected(position)
;
}
}
public void
setOnPageChangeListener
(ViewPager.OnPageChangeListener listener) {
this
.
mListener
= listener
;
}
protected void
onMeasure
(
int
widthMeasureSpec
, int
heightMeasureSpec) {
this
.setMeasuredDimension(
this
.measureWidth(widthMeasureSpec)
, this
.measureHeight(heightMeasureSpec))
;
}
private int
measureWidth
(
int
measureSpec) {
int
specMode = MeasureSpec.
getMode
(measureSpec)
;
int
specSize = MeasureSpec.
getSize
(measureSpec)
;
float
result
;
if
(specMode !=
1073741824
&&
this
.
mViewPager
!=
null
) {
int
count =
this
.
mViewPager
.getAdapter().getCount()
;
result = (
float
)(
this
.getPaddingLeft() +
this
.getPaddingRight()) + (
float
)(count *
this
.
mLineWidth
)+
20
+ + (
float
)(count -
1
) *
this
.
mGapWidth
;
if
(specMode == -
2147483648
) {
result = Math.
min
(result
,
(
float
)specSize)
;
}
}
else
{
result = (
float
)specSize
;
}
return
(
int
) Math.
ceil
(result)
;
}
private int
measureHeight
(
int
measureSpec) {
int
specMode = MeasureSpec.
getMode
(measureSpec)
;
int
specSize = MeasureSpec.
getSize
(measureSpec)
;
float
result
;
if
(specMode ==
1073741824
) {
result = (
float
)specSize
;
}
else
{
result =
this
.
mPaintSelected
.getStrokeWidth() + (
float
)
this
.getPaddingTop() + (
float
)
this
.getPaddingBottom()
;
if
(specMode == -
2147483648
) {
result = Math.
min
(result
,
(
float
)specSize)
;
}
}
return
(
int
) Math.
ceil
(result)
;
}
public void
onRestoreInstanceState
(Parcelable state) {
CustomLinePageIndicator.SavedState savedState = (CustomLinePageIndicator.SavedState)state
;
super
.onRestoreInstanceState(savedState.getSuperState())
;
this
.
mCurrentPage
= savedState.
currentPage
;
this
.requestLayout()
;
}
public
Parcelable
onSaveInstanceState
() {
Parcelable superState =
super
.onSaveInstanceState()
;
CustomLinePageIndicator.SavedState savedState =
new
CustomLinePageIndicator.SavedState(superState)
;
savedState.
currentPage
=
this
.
mCurrentPage
;
return
savedState
;
}
static class
SavedState
extends
View.BaseSavedState {
int
currentPage
;
public static final
Creator<CustomLinePageIndicator.SavedState>
CREATOR
=
new
Creator<CustomLinePageIndicator.SavedState>() {
public
CustomLinePageIndicator.SavedState
createFromParcel
(Parcel in) {
return new
CustomLinePageIndicator.SavedState(in)
;
}
public
CustomLinePageIndicator.SavedState[]
newArray
(
int
size) {
return new
CustomLinePageIndicator.SavedState[size]
;
}
}
;
public
SavedState
(Parcelable superState) {
super
(superState)
;
}
private
SavedState
(Parcel in) {
super
(in)
;
this
.
currentPage
= in.readInt()
;
}
public void
writeToParcel
(Parcel dest
, int
flags) {
super
.writeToParcel(dest
,
flags)
;
dest.writeInt(
this
.
currentPage
)
;
}
}
}
一:
自定义View之onMeasure()
可以说重载onMeasure(),onLayout(),onDraw()三个函数构建了自定义View的外观形象。再加上onTouchEvent()等重载视图的行为,可以构建任何我们需要的可感知到的自定义View。
本节我们探索自定义View中onMeasure()起到了什么样的作用,题外要插的一句是,Activity框架,View框架中大量的on函数基本上都应用到了Template模式,掌握这一模式对于理解这些框架大有裨益。
我们知道,不管是自定义View还是系统提供的TextView这些,它们都必须放置在LinearLayout等一些ViewGroup中,因此理论上我们可以很好的理解onMeasure(),onLayout(),onDraw()这三个函数:1.View本身大小多少,这由onMeasure()决定;2.View在ViewGroup中的位置如何,这由onLayout()决定;3.绘制View,onDraw()定义了如何绘制这个View。
首先我们要理解的是widthMeasureSpec, heightMeasureSpec这两个参数是从哪里来的?onMeasure()函数由包含这个View的具体的ViewGroup调用,因此值也是从这个ViewGroup中传入的。这里我直接给出答案:子类View的这两个参数,由ViewGroup中的layout_width,layout_height和padding以及View自身的layout_margin共同决定。权值weight也是尤其需要考虑的因素,有它的存在情况可能会稍微复杂点。
二:
ViewConfiguration.getScaledTouchSlop ()
三:
实现这种不等长圆角PageIndicator
CustomLinePageIndicator
mPaintSelected.setStrokeCap(Paint.Cap.ROUND);//处理圆角
mPaintUnselected.setStrokeCap(Paint.Cap.ROUND);
//处理不等长
for
(
int
i =
0
;
i < count
;
++i) {
float
dx1 = i >
this
.
mCurrentPage
?horizontalOffset + (
float
)i * lineWidthAndGap+
20
:horizontalOffset + (
float
)i * lineWidthAndGap
;
float
dx2 = dx1 + (i ==
this
.
mCurrentPage
?
this
.
mLineWidth
+
20
:
this
.
mLineWidth
)
;
canvas.drawLine(dx1
,
verticalOffset
,
dx2
,
verticalOffset
,
i ==
this
.
mCurrentPage
?
this
.
mPaintSelected
:
this
.
mPaintUnselected
)
;
}
完整代码:
import
android.content.Context
;
import
android.content.res.Resources
;
import
android.content.res.TypedArray
;
import
android.graphics.Canvas
;
import
android.graphics.Paint
;
import
android.graphics.drawable.Drawable
;
import
android.os.Parcel
;
import
android.os.Parcelable
;
import
android.support.v4.view.MotionEventCompat
;
import
android.support.v4.view.PagerAdapter
;
import
android.support.v4.view.ViewConfigurationCompat
;
import
android.support.v4.view.ViewPager
;
import
android.util.AttributeSet
;
import
android.util.FloatMath
;
import
android.view.MotionEvent
;
import
android.view.View
;
import
android.view.ViewConfiguration
;
import
com.viewpagerindicator.LinePageIndicator
;
import
com.viewpagerindicator.PageIndicator
;
/**
* Created by chao on 2017/11/22.
*/
public class
CustomLinePageIndicator
extends
View
implements
PageIndicator {
private static final int
INVALID_POINTER
= -
1
;
private final
Paint
mPaintUnselected
;
private final
Paint
mPaintSelected
;
private
ViewPager
mViewPager
;
private
ViewPager.OnPageChangeListener
mListener
;
private int
mCurrentPage
;
private boolean
mCentered
;
private float
mLineWidth
;
private float
mGapWidth
;
private int
mTouchSlop
;
private float
mLastMotionX
;
private int
mActivePointerId
;
private boolean
mIsDragging
;
public
CustomLinePageIndicator
(Context context) {
this
(context
,
(AttributeSet)
null
)
;
}
public
CustomLinePageIndicator
(Context context
,
AttributeSet attrs) {
this
(context
,
attrs
,
com.viewpagerindicator.R.attr.
vpiLinePageIndicatorStyle
)
;
}
public
CustomLinePageIndicator
(Context context
,
AttributeSet attrs
, int
defStyle) {
super
(context
,
attrs
,
defStyle)
;
this
.
mPaintUnselected
=
new
Paint(
1
)
;
mPaintUnselected
.setStrokeCap(Paint.Cap.
ROUND
)
;
this
.
mPaintSelected
=
new
Paint(
1
)
;
mPaintSelected
.setStrokeCap(Paint.Cap.
ROUND
)
;
this
.
mLastMotionX
= -
1.0F
;
this
.
mActivePointerId
= -
1
;
if
(!
this
.isInEditMode()) {
Resources res =
this
.getResources()
;
int
defaultSelectedColor = res.getColor(com.viewpagerindicator.R.color.
default_line_indicator_selected_color
)
;
int
defaultUnselectedColor = res.getColor(com.viewpagerindicator.R.color.
default_line_indicator_unselected_color
)
;
float
defaultLineWidth = res.getDimension(com.viewpagerindicator.R.dimen.
default_line_indicator_line_width
)-
5
;
float
defaultGapWidth = res.getDimension(com.viewpagerindicator.R.dimen.
default_line_indicator_gap_width
)+
20
;
float
defaultStrokeWidth = res.getDimension(com.viewpagerindicator.R.dimen.
default_line_indicator_stroke_width
)
;
boolean
defaultCentered = res.getBoolean(com.viewpagerindicator.R.bool.
default_line_indicator_centered
)
;
TypedArray a = context.obtainStyledAttributes(attrs
,
com.viewpagerindicator.R.styleable.
LinePageIndicator
,
defStyle
,
0
)
;
this
.
mCentered
= a.getBoolean(
1
,
defaultCentered)
;
this
.
mLineWidth
= a.getDimension(
5
,
defaultLineWidth)
;
this
.
mGapWidth
= a.getDimension(
6
,
defaultGapWidth)
;
this
.setStrokeWidth(a.getDimension(
3
,
defaultStrokeWidth+
5
))
;
this
.
mPaintUnselected
.setColor(a.getColor(
4
,
defaultUnselectedColor))
;
this
.
mPaintSelected
.setColor(a.getColor(
2
,
defaultSelectedColor))
;
Drawable background = a.getDrawable(
0
)
;
if
(background !=
null
) {
this
.setBackgroundDrawable(background)
;
}
a.recycle()
;
ViewConfiguration configuration = ViewConfiguration.
get
(context)
;
this
.
mTouchSlop
= ViewConfigurationCompat.
getScaledPagingTouchSlop
(configuration)
;
}
}
public void
setCentered
(
boolean
centered) {
this
.
mCentered
= centered
;
this
.invalidate()
;
}
public boolean
isCentered
() {
return this
.
mCentered
;
}
public void
setUnselectedColor
(
int
unselectedColor) {
this
.
mPaintUnselected
.setColor(unselectedColor)
;
this
.invalidate()
;
}
public int
getUnselectedColor
() {
return this
.
mPaintUnselected
.getColor()
;
}
public void
setSelectedColor
(
int
selectedColor) {
this
.
mPaintSelected
.setColor(selectedColor)
;
this
.invalidate()
;
}
public int
getSelectedColor
() {
return this
.
mPaintSelected
.getColor()
;
}
public void
setLineWidth
(
float
lineWidth) {
this
.
mLineWidth
= lineWidth
;
this
.invalidate()
;
}
public float
getLineWidth
() {
return this
.
mLineWidth
;
}
public void
setStrokeWidth
(
float
lineHeight) {
this
.
mPaintSelected
.setStrokeWidth(lineHeight)
;
this
.
mPaintUnselected
.setStrokeWidth(lineHeight)
;
this
.invalidate()
;
}
public float
getStrokeWidth
() {
return this
.
mPaintSelected
.getStrokeWidth()
;
}
public void
setGapWidth
(
float
gapWidth) {
this
.
mGapWidth
= gapWidth
;
this
.invalidate()
;
}
public float
getGapWidth
() {
return this
.
mGapWidth
;
}
protected void
onDraw
(Canvas canvas) {
super
.onDraw(canvas)
;
if
(
this
.
mViewPager
!=
null
) {
int
count =
this
.
mViewPager
.getAdapter().getCount()
;
if
(count !=
0
) {
if
(
this
.
mCurrentPage
>= count) {
this
.setCurrentItem(count -
1
)
;
}
else
{
float
lineWidthAndGap =
this
.
mLineWidth
+
this
.
mGapWidth
;
float
indicatorWidth = (
float
)count * lineWidthAndGap -
this
.
mGapWidth
;
float
paddingTop = (
float
)
this
.getPaddingTop()
;
float
paddingLeft = (
float
)
this
.getPaddingLeft()
;
float
paddingRight = (
float
)
this
.getPaddingRight()
;
float
verticalOffset = paddingTop + ((
float
)
this
.getHeight() - paddingTop - (
float
)
this
.getPaddingBottom()) /
2.0F
;
float
horizontalOffset = paddingLeft
;
if
(
this
.
mCentered
) {
horizontalOffset = paddingLeft + (((
float
)
this
.getWidth() - paddingLeft - paddingRight) /
2.0F
- indicatorWidth /
2.0F
)
;
}
for
(
int
i =
0
;
i < count
;
++i) {
float
dx1 = i >
this
.
mCurrentPage
?horizontalOffset + (
float
)i * lineWidthAndGap+
15
:horizontalOffset + (
float
)i * lineWidthAndGap
;
float
dx2 = dx1 + (i ==
this
.
mCurrentPage
?
this
.
mLineWidth
+
15
:
this
.
mLineWidth
)
;
canvas.drawLine(dx1
,
verticalOffset
,
dx2
,
verticalOffset
,
i ==
this
.
mCurrentPage
?
this
.
mPaintSelected
:
this
.
mPaintUnselected
)
;
}
}
}
}
}
public boolean
onTouchEvent
(MotionEvent ev) {
if
(
super
.onTouchEvent(ev)) {
return true;
}
else if
(
this
.
mViewPager
!=
null
&&
this
.
mViewPager
.getAdapter().getCount() !=
0
) {
int
action = ev.getAction() &
255
;
int
count
;
int
width
;
float
deltaX
;
switch
(action) {
case
0
:
this
.
mActivePointerId
= MotionEventCompat.
getPointerId
(ev
,
0
)
;
this
.
mLastMotionX
= ev.getX()
;
break;
case
1
:
case
3
:
if
(!
this
.
mIsDragging
) {
count =
this
.
mViewPager
.getAdapter().getCount()
;
width =
this
.getWidth()
;
deltaX = (
float
)width /
2.0F
;
float
sixthWidth = (
float
)width /
6.0F
;
if
(
this
.
mCurrentPage
>
0
&& ev.getX() < deltaX - sixthWidth) {
if
(action !=
3
) {
this
.
mViewPager
.setCurrentItem(
this
.
mCurrentPage
-
1
)
;
}
return true;
}
if
(
this
.
mCurrentPage
< count -
1
&& ev.getX() > deltaX + sixthWidth) {
if
(action !=
3
) {
this
.
mViewPager
.setCurrentItem(
this
.
mCurrentPage
+
1
)
;
}
return true;
}
}
this
.
mIsDragging
=
false;
this
.
mActivePointerId
= -
1
;
if
(
this
.
mViewPager
.isFakeDragging()) {
this
.
mViewPager
.endFakeDrag()
;
}
break;
case
2
:
count = MotionEventCompat.
findPointerIndex
(ev
, this
.
mActivePointerId
)
;
float
x = MotionEventCompat.
getX
(ev
,
count)
;
deltaX = x -
this
.
mLastMotionX
;
if
(!
this
.
mIsDragging
&& Math.
abs
(deltaX) > (
float
)
this
.
mTouchSlop
) {
this
.
mIsDragging
=
true;
}
if
(
this
.
mIsDragging
) {
this
.
mLastMotionX
= x
;
if
(
this
.
mViewPager
.isFakeDragging() ||
this
.
mViewPager
.beginFakeDrag()) {
this
.
mViewPager
.fakeDragBy(deltaX)
;
}
}
case
4
:
default
:
break;
case
5
:
count = MotionEventCompat.
getActionIndex
(ev)
;
this
.
mLastMotionX
= MotionEventCompat.
getX
(ev
,
count)
;
this
.
mActivePointerId
= MotionEventCompat.
getPointerId
(ev
,
count)
;
break;
case
6
:
count = MotionEventCompat.
getActionIndex
(ev)
;
width = MotionEventCompat.
getPointerId
(ev
,
count)
;
if
(width ==
this
.
mActivePointerId
) {
int
newPointerIndex = count ==
0
?
1
:
0
;
this
.
mActivePointerId
= MotionEventCompat.
getPointerId
(ev
,
newPointerIndex)
;
}
this
.
mLastMotionX
= MotionEventCompat.
getX
(ev
,
MotionEventCompat.
findPointerIndex
(ev
, this
.
mActivePointerId
))
;
}
return true;
}
else
{
return false;
}
}
public void
setViewPager
(ViewPager viewPager) {
if
(
this
.
mViewPager
!= viewPager) {
if
(
this
.
mViewPager
!=
null
) {
this
.
mViewPager
.setOnPageChangeListener((ViewPager.OnPageChangeListener)
null
)
;
}
if
(viewPager.getAdapter() ==
null
) {
throw new
IllegalStateException(
"ViewPager does not have adapter instance."
)
;
}
else
{
this
.
mViewPager
= viewPager
;
this
.
mViewPager
.setOnPageChangeListener(
this
)
;
this
.invalidate()
;
}
}
}
public void
setViewPager
(ViewPager view
, int
initialPosition) {
this
.setViewPager(view)
;
this
.setCurrentItem(initialPosition)
;
}
public void
setCurrentItem
(
int
item) {
if
(
this
.
mViewPager
==
null
) {
throw new
IllegalStateException(
"ViewPager has not been bound."
)
;
}
else
{
this
.
mViewPager
.setCurrentItem(item)
;
this
.
mCurrentPage
= item
;
this
.invalidate()
;
}
}
public void
notifyDataSetChanged
() {
this
.invalidate()
;
}
public void
onPageScrollStateChanged
(
int
state) {
if
(
this
.
mListener
!=
null
) {
this
.
mListener
.onPageScrollStateChanged(state)
;
}
}
public void
onPageScrolled
(
int
position
, float
positionOffset
, int
positionOffsetPixels) {
if
(
this
.
mListener
!=
null
) {
this
.
mListener
.onPageScrolled(position
,
positionOffset
,
positionOffsetPixels)
;
}
}
public void
onPageSelected
(
int
position) {
this
.
mCurrentPage
= position
;
this
.invalidate()
;
if
(
this
.
mListener
!=
null
) {
this
.
mListener
.onPageSelected(position)
;
}
}
public void
setOnPageChangeListener
(ViewPager.OnPageChangeListener listener) {
this
.
mListener
= listener
;
}
protected void
onMeasure
(
int
widthMeasureSpec
, int
heightMeasureSpec) {
this
.setMeasuredDimension(
this
.measureWidth(widthMeasureSpec)
, this
.measureHeight(heightMeasureSpec))
;
}
private int
measureWidth
(
int
measureSpec) {
int
specMode = MeasureSpec.
getMode
(measureSpec)
;
int
specSize = MeasureSpec.
getSize
(measureSpec)
;
float
result
;
if
(specMode !=
1073741824
&&
this
.
mViewPager
!=
null
) {
int
count =
this
.
mViewPager
.getAdapter().getCount()
;
result = (
float
)(
this
.getPaddingLeft() +
this
.getPaddingRight()) + (
float
)(count *
this
.
mLineWidth
)+
20
+ + (
float
)(count -
1
) *
this
.
mGapWidth
;
if
(specMode == -
2147483648
) {
result = Math.
min
(result
,
(
float
)specSize)
;
}
}
else
{
result = (
float
)specSize
;
}
return
(
int
) Math.
ceil
(result)
;
}
private int
measureHeight
(
int
measureSpec) {
int
specMode = MeasureSpec.
getMode
(measureSpec)
;
int
specSize = MeasureSpec.
getSize
(measureSpec)
;
float
result
;
if
(specMode ==
1073741824
) {
result = (
float
)specSize
;
}
else
{
result =
this
.
mPaintSelected
.getStrokeWidth() + (
float
)
this
.getPaddingTop() + (
float
)
this
.getPaddingBottom()
;
if
(specMode == -
2147483648
) {
result = Math.
min
(result
,
(
float
)specSize)
;
}
}
return
(
int
) Math.
ceil
(result)
;
}
public void
onRestoreInstanceState
(Parcelable state) {
CustomLinePageIndicator.SavedState savedState = (CustomLinePageIndicator.SavedState)state
;
super
.onRestoreInstanceState(savedState.getSuperState())
;
this
.
mCurrentPage
= savedState.
currentPage
;
this
.requestLayout()
;
}
public
Parcelable
onSaveInstanceState
() {
Parcelable superState =
super
.onSaveInstanceState()
;
CustomLinePageIndicator.SavedState savedState =
new
CustomLinePageIndicator.SavedState(superState)
;
savedState.
currentPage
=
this
.
mCurrentPage
;
return
savedState
;
}
static class
SavedState
extends
View.BaseSavedState {
int
currentPage
;
public static final
Creator<CustomLinePageIndicator.SavedState>
CREATOR
=
new
Creator<CustomLinePageIndicator.SavedState>() {
public
CustomLinePageIndicator.SavedState
createFromParcel
(Parcel in) {
return new
CustomLinePageIndicator.SavedState(in)
;
}
public
CustomLinePageIndicator.SavedState[]
newArray
(
int
size) {
return new
CustomLinePageIndicator.SavedState[size]
;
}
}
;
public
SavedState
(Parcelable superState) {
super
(superState)
;
}
private
SavedState
(Parcel in) {
super
(in)
;
this
.
currentPage
= in.readInt()
;
}
public void
writeToParcel
(Parcel dest
, int
flags) {
super
.writeToParcel(dest
,
flags)
;
dest.writeInt(
this
.
currentPage
)
;
}
}
}