diff --git a/app/src/main/java/com/github/xiaogegechen/trackview/MainActivity.java b/app/src/main/java/com/github/xiaogegechen/trackview/MainActivity.java index 790afad..0387a21 100644 --- a/app/src/main/java/com/github/xiaogegechen/trackview/MainActivity.java +++ b/app/src/main/java/com/github/xiaogegechen/trackview/MainActivity.java @@ -5,7 +5,6 @@ import android.util.Log; import android.view.View; import android.widget.TextView; -import android.widget.Toast; import com.github.xiaogegechen.library.TrackView; @@ -13,6 +12,8 @@ public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; + private boolean state = false; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate (savedInstanceState); @@ -21,6 +22,7 @@ protected void onCreate(Bundle savedInstanceState) { final TrackView trackView = findViewById (R.id.track_view); final TextView textView = findViewById (R.id.text_view); + textView.setOnClickListener (new View.OnClickListener () { @Override public void onClick(View v) { @@ -31,8 +33,16 @@ public void onClick(View v) { trackView.setOnClickListener (new View.OnClickListener () { @Override public void onClick(View v) { - textView.setText ("WORLD"); - Toast.makeText (MainActivity.this, "点击了拖动按钮", Toast.LENGTH_SHORT).show (); + + TrackView.Position position = trackView.getPosition (); + Log.d (TAG, "onClick: " + position); + + if(!state){ + trackView.close (); + }else{ + trackView.open (); + } + state = !state; } }); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index cbe070a..74849a4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,21 +1,24 @@ + + android:text="HELLO" + android:textSize="50sp" /> + app:out_stroke_color="#000000" + app:out_stroke_width="1dp" + app:inner_text_size="30sp" + app:inner_text_color="@color/colorAccent" + app:inner_text="hello"/> \ No newline at end of file diff --git a/library/src/main/java/com/github/xiaogegechen/library/TrackView.java b/library/src/main/java/com/github/xiaogegechen/library/TrackView.java index 6138d72..209ad51 100644 --- a/library/src/main/java/com/github/xiaogegechen/library/TrackView.java +++ b/library/src/main/java/com/github/xiaogegechen/library/TrackView.java @@ -1,6 +1,9 @@ package com.github.xiaogegechen.library; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -8,11 +11,14 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.RectF; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Log; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; /** @@ -20,6 +26,8 @@ */ public class TrackView extends View { + private static final String TAG = "TrackView"; + // 默认大小 private static final int SIZE_DEFAULT = 50; @@ -29,8 +37,8 @@ public class TrackView extends View { // 松手后回到侧边的默认时长 private static final int GO_TO_BOUNDARY_INTERVAL_DEFAULT = 100; - // 默认的外围padding - private static final int PADDING_DEFAULT = 3; + // 合上的默认时长 + private static final int CLOSE_INTERVAL_DEFAULT = 500; // 默认控件活动边界留白 private static final int BLANK_LEFT_DEFAULT = 10; @@ -59,18 +67,40 @@ public class TrackView extends View { // 默认内部填充的颜色 private static final int IN_CONTENT_COLOR_DEFAULT = Color.WHITE; - // 外部圆的画笔 + // 默认内部文字颜色 + private static final int IN_TEXT_COLOR_DEFAULT = Color.BLACK; + + // 默认内部文字大小sp + private static final int IN_TEXT_SIZE_DEFAULT = 16; + + // 默认内部文字 + private static final String IN_TEXT_DEFAULT = ""; + + // 外部轮廓的画笔 private Paint mOutPaint; + // 外部轮廓的路径 + private Path mOutPath; + // 内部线的画笔 private Paint mInPaint; // 内部填涂色的画笔 private Paint mInContentPaint; + // 内部填涂色的路径 + private Path mInContentPath; + + // 内部文字的画笔 + private Paint mInTextPaint; + // 内部的path private Path mInPath; + // 左右两个半圆的外界矩形 + private RectF mLeftRectF; + private RectF mRightRectF; + // 外部的属性 private int mOutHeight; private int mOutWidth; @@ -83,6 +113,9 @@ public class TrackView extends View { private int mInnerStrokeColor; private int mInnerStrokeWidth; private int mInnerContentColor; + private int mInnerTextColor; + private int mInnerTextSize; + private String mInnerText; // 边界属性, 单位是pixel private int mBlankLeft; @@ -90,10 +123,13 @@ public class TrackView extends View { private int mBlankTop; private int mBlankBottom; - // 上一次的位置 + // 上一次的触摸点的位置 private int mLastX = 0; private int mLastY = 0; + // 上一次view的宽度 + private int mLastWidth = 0; + // 在这个view内部,触摸位置距离view左上角的距离 private int mDisX; private int mDisY; @@ -107,10 +143,33 @@ public class TrackView extends View { // 根据当前这个事件序列判定的模式 private Mode mMode = Mode.NONE; + // 当前的位置 + private Position mPosition = Position.LEFT; + + // 动画监听器 + private AnimatorListenerAdapter mOpenAnimatorListenerAdapter; + private ValueAnimator.AnimatorUpdateListener mOpenAnimatorUpdateListener; + private AnimatorListenerAdapter mCloseAnimatorListenerAdapter; + private ValueAnimator.AnimatorUpdateListener mCloseAnimatorUpdateListener; + // 是否需要调整位置,因为初始的时候如果没有触摸事件,该view不受边界约束, // 因此需要在渲染结束后进行微调,这个变量记录是否进行了微调。 private boolean mAlreadyAdjust = false; + // 是否正在动画 + private boolean mIsInAnimation = false; + + // 当前是否是圆形 + private boolean mIsClosed = false; + + // 是否可以执行展开和闭合的动画,针对刚开始高就大于宽的情况, + // 它是不应该有展开和闭合的能力的 + private boolean mCanDoAnimation = false; + + // 起始宽度和终止宽度 + private int mOriginWidth =0; + private int mEndWidth =0; + // 屏幕像素 private int mScreenWidthInPixel; private int mScreenHeightInPixel; @@ -135,19 +194,25 @@ public TrackView(Context context, @Nullable AttributeSet attrs, int defStyleAttr TypedArray ta = context.obtainStyledAttributes (attrs, R.styleable.TrackView); // 拿到属性值 - mInnerDistance = ta.getDimensionPixelSize (R.styleable.TrackView_inner_distance, IN_DISTANCE_DEFAULT); - mInnerLength = ta.getDimensionPixelSize (R.styleable.TrackView_inner_length, IN_LENGTH_DEFAULT); + mInnerDistance = ta.getDimensionPixelSize (R.styleable.TrackView_inner_distance, dp2px (IN_DISTANCE_DEFAULT)); + mInnerLength = ta.getDimensionPixelSize (R.styleable.TrackView_inner_length, dp2px (IN_LENGTH_DEFAULT)); mInnerStrokeColor = ta.getColor (R.styleable.TrackView_inner_stroke_color, IN_STROKE_COLOR_DEFAULT); - mInnerStrokeWidth = ta.getDimensionPixelSize (R.styleable.TrackView_inner_stroke_width, IN_STROKE_WIDTH_DEFAULT); + mInnerStrokeWidth = ta.getDimensionPixelSize (R.styleable.TrackView_inner_stroke_width, dp2px (IN_STROKE_WIDTH_DEFAULT)); mInnerContentColor = ta.getColor (R.styleable.TrackView_inner_content_color, IN_CONTENT_COLOR_DEFAULT); + mInnerTextColor = ta.getColor (R.styleable.TrackView_inner_text_color, IN_TEXT_COLOR_DEFAULT); + mInnerTextSize = ta.getDimensionPixelSize (R.styleable.TrackView_inner_text_size, sp2px (IN_TEXT_SIZE_DEFAULT)); + mInnerText = ta.getString (R.styleable.TrackView_inner_text); + if(mInnerText == null){ + mInnerText = IN_TEXT_DEFAULT; + } mOutStrokeColor = ta.getColor (R.styleable.TrackView_out_stroke_color, OUT_STROKE_COLOR_DEFAULT); - mOutStrokeWidth = ta.getDimensionPixelSize (R.styleable.TrackView_out_stroke_width, OUT_STROKE_WIDTH_DEFAULT); + mOutStrokeWidth = ta.getDimensionPixelSize (R.styleable.TrackView_out_stroke_width, dp2px (OUT_STROKE_WIDTH_DEFAULT)); - mBlankLeft = dp2px (ta.getDimensionPixelSize (R.styleable.TrackView_blank_left, BLANK_LEFT_DEFAULT)); - mBlankRight = dp2px (ta.getDimensionPixelSize (R.styleable.TrackView_blank_right, BLANK_RIGHT_DEFAULT)); - mBlankTop = dp2px (ta.getDimensionPixelSize (R.styleable.TrackView_blank_top, BLANK_TOP_DEFAULT)); - mBlankBottom = dp2px (ta.getDimensionPixelSize (R.styleable.TrackView_blank_bottom, BLANK_BOTTOM_DEFAULT)); + mBlankLeft = ta.getDimensionPixelSize (R.styleable.TrackView_blank_left, dp2px (BLANK_LEFT_DEFAULT)); + mBlankRight = ta.getDimensionPixelSize (R.styleable.TrackView_blank_right, dp2px (BLANK_RIGHT_DEFAULT)); + mBlankTop = ta.getDimensionPixelSize (R.styleable.TrackView_blank_top, dp2px (BLANK_TOP_DEFAULT)); + mBlankBottom = ta.getDimensionPixelSize (R.styleable.TrackView_blank_bottom, dp2px (BLANK_BOTTOM_DEFAULT)); // 回收typedArray ta.recycle (); @@ -157,54 +222,90 @@ public TrackView(Context context, @Nullable AttributeSet attrs, int defStyleAttr @Override protected void onDraw(Canvas canvas) { - super.onDraw (canvas); - - // 外部圆的尺寸 - mOutWidth = getWidth () - getPaddingLeft () - getPaddingRight () - dp2px (PADDING_DEFAULT); - mOutHeight = getHeight () - getPaddingBottom () -getPaddingTop () - dp2px (PADDING_DEFAULT); - - /** - * * * - * * * mInLength - * * * - * * - * - * mInDistance - * - * * * - * * * - * * * - * * - * - */ - mInPath.moveTo ((float)((getWidth () / 2) - (mInnerLength / Math.sqrt (2))), - (float)(getHeight () / 2 - (mInnerLength / Math.sqrt (2) + mInnerDistance) / 2)); - - mInPath.lineTo ((float) getWidth () / 2, - (float)(getHeight () / 2 - (mInnerLength / Math.sqrt (2) + mInnerDistance) / 2 + mInnerLength / Math.sqrt (2))); - - mInPath.lineTo ((float)((getWidth () / 2) + (mInnerLength / Math.sqrt (2))), - (float)(getHeight () / 2 - (mInnerLength / Math.sqrt (2) + mInnerDistance) / 2)); - - mInPath.moveTo ((float)((getWidth () / 2) - (mInnerLength / Math.sqrt (2))), - (float)(getHeight () / 2 - (mInnerLength / Math.sqrt (2) + mInnerDistance) / 2 + mInnerDistance)); - - mInPath.lineTo ((float) getWidth () / 2, - (float)((float)(getHeight () / 2) - (mInnerLength / Math.sqrt (2) + mInnerDistance) / 2 + mInnerLength / Math.sqrt (2) + mInnerDistance)); - - mInPath.lineTo ((float)((getWidth () / 2) + (mInnerLength / Math.sqrt (2))), - (float)(getHeight () / 2 - (mInnerLength / Math.sqrt (2) + mInnerDistance) / 2 + mInnerDistance)); - - canvas.drawCircle (getWidth () >> 1, getHeight () >> 1, Math.min (mOutWidth >> 1, mOutHeight >> 1), mOutPaint); - canvas.drawCircle (getWidth () >> 1, getHeight () >> 1, Math.min (mOutWidth >> 1, mOutHeight >> 1), mInContentPaint); - canvas.drawPath (mInPath, mInPaint); + + // 外部形状的尺寸 + mOutWidth = getWidth () - getPaddingLeft () - getPaddingRight (); + mOutHeight = getHeight () - getPaddingBottom () -getPaddingTop (); + + if(mOutHeight >= mOutWidth){ + + // 只画圆 + /** + * * * + * * * mInLength + * * * + * * + * + * mInDistance + * + * * * + * * * + * * * + * * + * + */ + mInPath.moveTo ((float) (mOutHeight/2 + getPaddingLeft ()-mInnerLength / Math.sqrt (2)), (float) (getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2)); + mInPath.lineTo ((float) (mOutHeight/2 + getPaddingLeft ()), (float) (mInnerLength / Math.sqrt (2)+getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2)); + mInPath.lineTo ((float) (mOutHeight/2 + getPaddingLeft ()-mInnerLength / Math.sqrt (2)+mInnerLength*Math.sqrt (2)), (float) (getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2)); + mInPath.moveTo ((float) (mOutHeight/2 + getPaddingLeft ()-mInnerLength / Math.sqrt (2)), (float) (getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2+mInnerDistance)); + mInPath.lineTo ((float) (mOutHeight/2 + getPaddingLeft ()), (float) (mInnerLength / Math.sqrt (2)+getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2+mInnerDistance)); + mInPath.lineTo ((float) (mOutHeight/2 + getPaddingLeft ()-mInnerLength / Math.sqrt (2)+mInnerLength*Math.sqrt (2)), (float) (getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2+mInnerDistance)); + + canvas.drawCircle (getPaddingLeft ()+mOutWidth/2, getPaddingTop ()+mOutWidth/2, mOutWidth/2, mOutPaint); + canvas.drawCircle (getPaddingLeft ()+mOutWidth/2, getPaddingTop ()+mOutWidth/2, mOutWidth/2, mInContentPaint); + canvas.drawPath (mInPath, mInPaint); + }else{ + mCanDoAnimation = true; + + // 拿到起始宽度和终止宽度 + if(mOriginWidth == 0){ + mOriginWidth = getWidth (); + } + + if(mEndWidth == 0){ + mEndWidth = getWidth () - (mOutWidth - mOutHeight); + } + + mLeftRectF.set (getPaddingLeft (), getPaddingTop (), getPaddingLeft ()+mOutHeight, getPaddingTop ()+mOutHeight); + mRightRectF.set (getPaddingLeft ()+mOutWidth-mOutHeight, getPaddingTop (), getPaddingLeft ()+mOutWidth, getPaddingTop ()+mOutHeight); + + // 整个view的填充色 + mInContentPath.moveTo (mOutHeight/2 + getPaddingLeft ()+mOutWidth-mOutHeight, mOutHeight+getPaddingTop ()); + mInContentPath.lineTo (mOutHeight/2+getPaddingLeft (), mOutHeight+getPaddingTop ()); + mInContentPath.arcTo (mLeftRectF, 90, 180); + mInContentPath.lineTo (mOutWidth+getPaddingLeft ()-mOutHeight/2, getPaddingTop ()); + mInContentPath.arcTo (mRightRectF, 270, 180); + canvas.drawPath (mInContentPath, mInContentPaint); + + // 外轮廓路径 + mOutPath.moveTo (mOutHeight/2 + getPaddingLeft ()+mOutWidth-mOutHeight, mOutHeight+getPaddingTop ()); + mOutPath.lineTo (mOutHeight/2+getPaddingLeft (), mOutHeight+getPaddingTop ()); + mOutPath.arcTo (mLeftRectF, 90, 180); + mOutPath.lineTo (mOutWidth+getPaddingLeft ()-mOutHeight/2, getPaddingTop ()); + if(!mIsInAnimation){ + //闭合并画出文字 + mOutPath.arcTo (mRightRectF, 270, 180); + float x = mOutWidth / 2 + getPaddingLeft (); + float y = mOutHeight / 2 + getPaddingTop () - mInTextPaint.getFontMetrics ().top / 2 - mInTextPaint.getFontMetrics ().bottom / 2; + canvas.drawText (mInnerText, x,y,mInTextPaint); + } + canvas.drawPath (mOutPath, mOutPaint); + + //内部图案路径 + mInPath.moveTo ((float) (mOutHeight/2 + getPaddingLeft ()-mInnerLength / Math.sqrt (2)), (float) (getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2)); + mInPath.lineTo ((float) (mOutHeight/2 + getPaddingLeft ()), (float) (mInnerLength / Math.sqrt (2)+getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2)); + mInPath.lineTo ((float) (mOutHeight/2 + getPaddingLeft ()-mInnerLength / Math.sqrt (2)+mInnerLength*Math.sqrt (2)), (float) (getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2)); + mInPath.moveTo ((float) (mOutHeight/2 + getPaddingLeft ()-mInnerLength / Math.sqrt (2)), (float) (getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2+mInnerDistance)); + mInPath.lineTo ((float) (mOutHeight/2 + getPaddingLeft ()), (float) (mInnerLength / Math.sqrt (2)+getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2+mInnerDistance)); + mInPath.lineTo ((float) (mOutHeight/2 + getPaddingLeft ()-mInnerLength / Math.sqrt (2)+mInnerLength*Math.sqrt (2)), (float) (getPaddingTop ()+mOutHeight/2-(mInnerDistance+mInnerLength / Math.sqrt (2))/2+mInnerDistance)); + canvas.drawPath (mInPath, mInPaint); + } // 如果需要微调的话,进行微调 if(!mAlreadyAdjust){ // 就近靠边 setTranslationX (mBlankLeft - getX ()); - mAlreadyAdjust = true; } } @@ -250,30 +351,60 @@ public boolean onTouchEvent(MotionEvent event) { // 点击事件不需要移动 performClick (); } else{ - if(x > mBlankLeft && x < (mScreenWidthInPixel - mBlankRight)){ + if(x > mBlankLeft && x < (mScreenWidthInPixel - mBlankRight) + && mPosition != Position.LEFT + && mPosition != Position.RIGHT){ - // 离开点在边界之外不需要移动 + // 离开点在边界之外不需要移动,已经在边界不需要移动 // 回到侧面 if(event.getRawX () < mScreenWidthInPixel / 2){ // 回到最左侧 - - ObjectAnimator.ofFloat (this, + ObjectAnimator animator = ObjectAnimator.ofFloat (this, "TranslationX", getX (), getTranslationX () + (-1 * (x - mDisX - mBlankLeft)) - ).setDuration (GO_TO_BOUNDARY_INTERVAL_DEFAULT).start (); - x = x - (x - mDisX - mBlankLeft); + ); + animator.setDuration (GO_TO_BOUNDARY_INTERVAL_DEFAULT); + + // 监听动画生命周期 + animator.addListener (new AnimatorListenerAdapter () { + @Override + public void onAnimationEnd(Animator animation) { + mPosition = Position.LEFT; + } + + @Override + public void onAnimationStart(Animator animation) { + mPosition = Position.FLYING; + } + }); + animator.start (); + }else{ // 回到最右侧 - ObjectAnimator.ofFloat (this, + ObjectAnimator animator = ObjectAnimator.ofFloat (this, "TranslationX", getX (), - getTranslationX () + ((mScreenWidthInPixel - mBlankRight) - (Math.min (mOutWidth, mOutHeight) - mDisX + x)) - ).setDuration (GO_TO_BOUNDARY_INTERVAL_DEFAULT).start (); + getTranslationX () + ((mScreenWidthInPixel - mBlankRight) - (getWidth () - mDisX + x)) + ); + + // 监听动画生命周期 + animator.addListener (new AnimatorListenerAdapter () { + @Override + public void onAnimationEnd(Animator animation) { + mPosition = Position.RIGHT; + } + + @Override + public void onAnimationStart(Animator animation) { + mPosition = Position.FLYING; + } + }); + animator.setDuration (GO_TO_BOUNDARY_INTERVAL_DEFAULT); + animator.start (); - x = x + (mScreenWidthInPixel - mBlankRight) - (Math.min (mOutWidth, mOutHeight) - mDisX + x); } } } @@ -292,7 +423,7 @@ public boolean onTouchEvent(MotionEvent event) { // 预测量的边距 int preXLeft = x - mDisX; - int preXRight = mScreenWidthInPixel - (x + Math.min (mOutWidth, mOutHeight) - mDisX); + int preXRight = mScreenWidthInPixel - (x + getWidth () - mDisX); int preYUp = y - mDisY; int preYDown = mScreenHeightInPixel - (y + Math.min (mOutWidth, mOutHeight) - mDisY); @@ -300,16 +431,19 @@ public boolean onTouchEvent(MotionEvent event) { if(preXLeft <= mBlankLeft){ // 超出左边界 + mPosition = Position.LEFT; dx = x - mLastX + mBlankLeft - preXLeft; x = x + mBlankLeft - preXLeft; }else if(preXRight <= mBlankRight){ // 超出右边界 + mPosition = Position.RIGHT; dx = x - mLastX - (mBlankRight - preXRight); x = x - (mBlankRight - preXRight); }else{ // 正常 + mPosition = Position.FLYING; dx = x - mLastX; } @@ -364,6 +498,149 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } } + // 合上 + public void close(){ + + // 不能执行动画,不处理 + if(!mCanDoAnimation){ + return; + } + + // 不是合上状态不处理,正在动画不处理 + if(mIsClosed || mIsInAnimation){ + return; + } + + // 正在移动不处理 + if(mPosition == Position.FLYING){ + return; + } + + ValueAnimator animator = ValueAnimator.ofInt (mOriginWidth, mEndWidth); + animator.setDuration (CLOSE_INTERVAL_DEFAULT); + + // 监听动画进度 + if (mCloseAnimatorUpdateListener == null) { + mCloseAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener () { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + ViewGroup.LayoutParams params = getLayoutParams (); + params.height = getHeight (); + params.width = (Integer) animation.getAnimatedValue (); + + //如果在右侧的话需要先改变位置,再改变大小 + if(mPosition == Position.RIGHT){ + setTranslationX (getTranslationX () + (mLastWidth - params.width)); + mLastWidth = params.width; + } + + // 改变view大小 + setLayoutParams (params); + } + }; + } + animator.addUpdateListener (mCloseAnimatorUpdateListener); + + // 监听动画生命周期 + if (mCloseAnimatorListenerAdapter == null) { + mCloseAnimatorListenerAdapter = new AnimatorListenerAdapter () { + + @Override + public void onAnimationStart(Animator animation) { + mLastWidth = getWidth (); + mIsInAnimation = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + mIsInAnimation = false; + mIsClosed = true; + + // 重置方便下次使用 + mLastWidth = 0; + } + }; + } + animator.addListener(mCloseAnimatorListenerAdapter); + animator.start (); + } + + // 展开 + public void open(){ + + // 不能执行动画,不处理 + if(!mCanDoAnimation){ + return; + } + + // 不是合上状态不处理,正在动画不处理 + if(!mIsClosed || mIsInAnimation){ + return; + } + + // 正在移动不处理 + if(mPosition == Position.FLYING){ + return; + } + + // 监听动画进度 + ValueAnimator animator = ValueAnimator.ofInt (mEndWidth, mOriginWidth); + animator.setDuration (CLOSE_INTERVAL_DEFAULT); + if (mOpenAnimatorUpdateListener == null) { + mOpenAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener () { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + ViewGroup.LayoutParams params = getLayoutParams (); + params.height = getHeight (); + params.width = (Integer) animation.getAnimatedValue (); + + //如果在右侧的话需要先改变位置,再改变大小 + if(mPosition == Position.RIGHT){ + setTranslationX (getTranslationX () + (mLastWidth - params.width)); + mLastWidth = params.width; + } + + // 改变view大小 + setLayoutParams (params); + } + }; + } + animator.addUpdateListener (mOpenAnimatorUpdateListener); + + // 监听动画生命周期 + if (mOpenAnimatorListenerAdapter == null) { + mOpenAnimatorListenerAdapter = new AnimatorListenerAdapter () { + + @Override + public void onAnimationStart(Animator animation) { + mIsInAnimation = true; + mLastWidth = getWidth (); + } + + @Override + public void onAnimationEnd(Animator animation) { + mIsInAnimation = false; + mIsClosed = false; + + // 重置方便下次使用 + mLastWidth = 0; + } + }; + } + animator.addListener (mOpenAnimatorListenerAdapter); + animator.start (); + } + + // 设置文字 + public void setText(String text){ + mInnerText = text; + invalidate (); + } + + public Position getPosition(){ + return mPosition; + } + /** * 该控件的三种模式,只要触发了ACTION_MOVE就是MOVE模式 * 没有触发ACTION_MOVE但是从ACTION_DOWN开始超过了500ms就是CANCEL模式 @@ -383,6 +660,18 @@ private enum Mode{ NONE } + /** + * view所处的位置 + * 上下边界暂时不考虑 + */ + public enum Position{ + LEFT, + RIGHT, + + // 正在滑翔 + FLYING + } + private void init() { mScreenWidthInPixel = getScreenParamsInPixel ()[0]; @@ -391,18 +680,33 @@ private void init() { mOutPaint = new Paint (); mInPaint = new Paint (); mInContentPaint = new Paint (); + mInTextPaint = new Paint (); + + mOutPath = new Path (); mInPath = new Path (); + mInContentPath = new Path (); + + mLeftRectF = new RectF (); + mRightRectF = new RectF (); mOutPaint.setStyle (Paint.Style.STROKE); mOutPaint.setColor (mOutStrokeColor); - mOutPaint.setStrokeWidth (dp2px(mOutStrokeWidth)); + mOutPaint.setStrokeWidth (mOutStrokeWidth); + mOutPaint.setAntiAlias(true); mInPaint.setStyle (Paint.Style.STROKE); mInPaint.setColor (mInnerStrokeColor); - mInPaint.setStrokeWidth(dp2px (mInnerStrokeWidth)); + mInPaint.setStrokeWidth(mInnerStrokeWidth); + mInPaint.setAntiAlias(true); mInContentPaint.setStyle (Paint.Style.FILL); mInContentPaint.setColor (mInnerContentColor); + + mInTextPaint.setTextAlign (Paint.Align.CENTER); + mInTextPaint.setStyle (Paint.Style.STROKE); + mInTextPaint.setColor (mInnerTextColor); + mInTextPaint.setTextSize (mInnerTextSize); + mInTextPaint.setAntiAlias(true); } /** @@ -412,6 +716,14 @@ private static int dp2px(float dpValue) { return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density); } + /** + * 根据手机的分辨率从 sp 的单位 转成为 px(像素) + */ + private int sp2px(float spValue) { + final float fontScale = mContext.getResources().getDisplayMetrics().scaledDensity; + return (int) (spValue * fontScale + 0.5f); + } + private int[] getScreenParamsInPixel(){ WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); @@ -421,4 +733,4 @@ private int[] getScreenParamsInPixel(){ return new int[]{width, height}; } -} +} \ No newline at end of file diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml index 5a9b15d..1c8c42a 100644 --- a/library/src/main/res/values/attrs.xml +++ b/library/src/main/res/values/attrs.xml @@ -8,6 +8,9 @@ + + +