初始化

This commit is contained in:
ouyang
2017-08-15 15:04:48 +08:00
parent f1de8d5d3e
commit 9412cc7505
3464 changed files with 236938 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
}
}
// gradle uploadArchives
//apply from: 'https://raw.githubusercontent.com/bingoogolapple/PublishAar/master/central-publish.gradle'
+3
View File
@@ -0,0 +1,3 @@
PUBLISH_AAR_ARTIFACT_ID=bga-badgeview
PUBLISH_AAR_DESCRIPTION=Android BadgeView Library
PUBLISH_AAR_GITHUB_REPOSITORIES_NAME=BGABadgeView-Android
+3
View File
@@ -0,0 +1,3 @@
<manifest package="cn.bingoogolapple.badgeview"/>
<!--
ss-->
@@ -0,0 +1,98 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/7 14:07
* 描述:
*/
public class BGABadgeFrameLayout extends FrameLayout implements BGABadgeable {
private BGABadgeViewHelper mBadgeViewHeler;
public BGABadgeFrameLayout(Context context) {
this(context, null);
}
public BGABadgeFrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BGABadgeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBadgeViewHeler = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightCenter);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mBadgeViewHeler.onTouchEvent(event);
}
@Override
public boolean callSuperOnTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
mBadgeViewHeler.drawBadge(canvas);
}
@Override
public void showCirclePointBadge() {
mBadgeViewHeler.showCirclePointBadge();
}
@Override
public void showTextBadge(String badgeText) {
mBadgeViewHeler.showTextBadge(badgeText);
}
@Override
public void hiddenBadge() {
mBadgeViewHeler.hiddenBadge();
}
@Override
public void showDrawableBadge(Bitmap bitmap) {
mBadgeViewHeler.showDrawable(bitmap);
}
@Override
public void setDragDismissDelegage(BGADragDismissDelegate delegate) {
mBadgeViewHeler.setDragDismissDelegage(delegate);
}
@Override
public boolean isShowBadge() {
return mBadgeViewHeler.isShowBadge();
}
@Override
public BGABadgeViewHelper getBadgeViewHelper() {
return mBadgeViewHeler;
}
}
@@ -0,0 +1,97 @@
/**
* Copyright 2015 bingoogolapple
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/6 下午3:04
* 描述:
*/
public class BGABadgeImageView extends ImageView implements BGABadgeable {
private BGABadgeViewHelper mBadgeViewHeler;
public BGABadgeImageView(Context context) {
this(context, null);
}
public BGABadgeImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BGABadgeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBadgeViewHeler = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightTop);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mBadgeViewHeler.onTouchEvent(event);
}
@Override
public boolean callSuperOnTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mBadgeViewHeler.drawBadge(canvas);
}
@Override
public void showCirclePointBadge() {
mBadgeViewHeler.showCirclePointBadge();
}
@Override
public void showTextBadge(String badgeText) {
mBadgeViewHeler.showTextBadge(badgeText);
}
@Override
public void hiddenBadge() {
mBadgeViewHeler.hiddenBadge();
}
@Override
public void showDrawableBadge(Bitmap bitmap) {
mBadgeViewHeler.showDrawable(bitmap);
}
@Override
public void setDragDismissDelegage(BGADragDismissDelegate delegate) {
mBadgeViewHeler.setDragDismissDelegage(delegate);
}
@Override
public boolean isShowBadge() {
return mBadgeViewHeler.isShowBadge();
}
@Override
public BGABadgeViewHelper getBadgeViewHelper() {
return mBadgeViewHeler;
}
}
@@ -0,0 +1,93 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.LinearLayout;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/7 14:07
* 描述:
*/
public class BGABadgeLinearLayout extends LinearLayout implements BGABadgeable {
private BGABadgeViewHelper mBadgeViewHeler;
public BGABadgeLinearLayout(Context context) {
this(context, null);
}
public BGABadgeLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mBadgeViewHeler = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightCenter);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mBadgeViewHeler.onTouchEvent(event);
}
@Override
public boolean callSuperOnTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
mBadgeViewHeler.drawBadge(canvas);
}
@Override
public void showCirclePointBadge() {
mBadgeViewHeler.showCirclePointBadge();
}
@Override
public void showTextBadge(String badgeText) {
mBadgeViewHeler.showTextBadge(badgeText);
}
@Override
public void hiddenBadge() {
mBadgeViewHeler.hiddenBadge();
}
@Override
public void showDrawableBadge(Bitmap bitmap) {
mBadgeViewHeler.showDrawable(bitmap);
}
@Override
public void setDragDismissDelegage(BGADragDismissDelegate delegate) {
mBadgeViewHeler.setDragDismissDelegage(delegate);
}
@Override
public boolean isShowBadge() {
return mBadgeViewHeler.isShowBadge();
}
@Override
public BGABadgeViewHelper getBadgeViewHelper() {
return mBadgeViewHeler;
}
}
@@ -0,0 +1,97 @@
/**
* Copyright 2015 bingoogolapple
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RadioButton;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/6 下午3:04
* 描述:
*/
public class BGABadgeRadioButton extends RadioButton implements BGABadgeable {
private BGABadgeViewHelper mBadgeViewHeler;
public BGABadgeRadioButton(Context context) {
this(context, null);
}
public BGABadgeRadioButton(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.radioButtonStyle);
}
public BGABadgeRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBadgeViewHeler = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightTop);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mBadgeViewHeler.onTouchEvent(event);
}
@Override
public boolean callSuperOnTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mBadgeViewHeler.drawBadge(canvas);
}
@Override
public void showCirclePointBadge() {
mBadgeViewHeler.showCirclePointBadge();
}
@Override
public void showTextBadge(String badgeText) {
mBadgeViewHeler.showTextBadge(badgeText);
}
@Override
public void hiddenBadge() {
mBadgeViewHeler.hiddenBadge();
}
@Override
public void showDrawableBadge(Bitmap bitmap) {
mBadgeViewHeler.showDrawable(bitmap);
}
@Override
public void setDragDismissDelegage(BGADragDismissDelegate delegate) {
mBadgeViewHeler.setDragDismissDelegage(delegate);
}
@Override
public boolean isShowBadge() {
return mBadgeViewHeler.isShowBadge();
}
@Override
public BGABadgeViewHelper getBadgeViewHelper() {
return mBadgeViewHeler;
}
}
@@ -0,0 +1,97 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/7 14:07
* 描述:
*/
public class BGABadgeRelativeLayout extends RelativeLayout implements BGABadgeable {
private BGABadgeViewHelper mBadgeViewHeler;
public BGABadgeRelativeLayout(Context context) {
this(context, null);
}
public BGABadgeRelativeLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BGABadgeRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBadgeViewHeler = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightCenter);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mBadgeViewHeler.onTouchEvent(event);
}
@Override
public boolean callSuperOnTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
mBadgeViewHeler.drawBadge(canvas);
}
@Override
public void showCirclePointBadge() {
mBadgeViewHeler.showCirclePointBadge();
}
@Override
public void showTextBadge(String badgeText) {
mBadgeViewHeler.showTextBadge(badgeText);
}
@Override
public void hiddenBadge() {
mBadgeViewHeler.hiddenBadge();
}
@Override
public void showDrawableBadge(Bitmap bitmap) {
mBadgeViewHeler.showDrawable(bitmap);
}
@Override
public void setDragDismissDelegage(BGADragDismissDelegate delegate) {
mBadgeViewHeler.setDragDismissDelegage(delegate);
}
@Override
public boolean isShowBadge() {
return mBadgeViewHeler.isShowBadge();
}
@Override
public BGABadgeViewHelper getBadgeViewHelper() {
return mBadgeViewHeler;
}
}
@@ -0,0 +1,97 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/6 下午3:04
* 描述:
*/
public class BGABadgeTextView extends TextView implements BGABadgeable {
private BGABadgeViewHelper mBadgeViewHeler;
public BGABadgeTextView(Context context) {
this(context, null);
}
public BGABadgeTextView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
public BGABadgeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBadgeViewHeler = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightCenter);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mBadgeViewHeler.onTouchEvent(event);
}
@Override
public boolean callSuperOnTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mBadgeViewHeler.drawBadge(canvas);
}
@Override
public void showCirclePointBadge() {
mBadgeViewHeler.showCirclePointBadge();
}
@Override
public void showTextBadge(String badgeText) {
mBadgeViewHeler.showTextBadge(badgeText);
}
@Override
public void hiddenBadge() {
mBadgeViewHeler.hiddenBadge();
}
@Override
public void showDrawableBadge(Bitmap bitmap) {
mBadgeViewHeler.showDrawable(bitmap);
}
@Override
public void setDragDismissDelegage(BGADragDismissDelegate delegate) {
mBadgeViewHeler.setDragDismissDelegage(delegate);
}
@Override
public boolean isShowBadge() {
return mBadgeViewHeler.isShowBadge();
}
@Override
public BGABadgeViewHelper getBadgeViewHelper() {
return mBadgeViewHeler;
}
}
@@ -0,0 +1,81 @@
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:16/3/14 上午2:46
* 描述:
*/
public class BGABadgeView extends View implements BGABadgeable {
private BGABadgeViewHelper mBadgeViewHeler;
public BGABadgeView(Context context) {
this(context, null);
}
public BGABadgeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BGABadgeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mBadgeViewHeler = new BGABadgeViewHelper(this, context, attrs, BGABadgeViewHelper.BadgeGravity.RightCenter);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mBadgeViewHeler.onTouchEvent(event);
}
@Override
public boolean callSuperOnTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mBadgeViewHeler.drawBadge(canvas);
}
@Override
public void showCirclePointBadge() {
mBadgeViewHeler.showCirclePointBadge();
}
@Override
public void showTextBadge(String badgeText) {
mBadgeViewHeler.showTextBadge(badgeText);
}
@Override
public void hiddenBadge() {
mBadgeViewHeler.hiddenBadge();
}
@Override
public void showDrawableBadge(Bitmap bitmap) {
mBadgeViewHeler.showDrawable(bitmap);
}
@Override
public void setDragDismissDelegage(BGADragDismissDelegate delegate) {
mBadgeViewHeler.setDragDismissDelegage(delegate);
}
@Override
public boolean isShowBadge() {
return mBadgeViewHeler.isShowBadge();
}
@Override
public BGABadgeViewHelper getBadgeViewHelper() {
return mBadgeViewHeler;
}
}
@@ -0,0 +1,513 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/6 下午10:13
* 描述:
*/
public class BGABadgeViewHelper {
private Bitmap mBitmap;
private BGABadgeable mBadgeable;
private Paint mBadgePaint;
/**
* 徽章背景色
*/
private int mBadgeBgColor;
/**
* 徽章文本的颜色
*/
private int mBadgeTextColor;
/**
* 徽章文本字体大小
*/
private int mBadgeTextSize;
/**
* 徽章背景与宿主控件上下边缘间距离
*/
private int mBadgeVerticalMargin;
/**
* 徽章背景与宿主控件左右边缘间距离
*/
private int mBadgeHorizontalMargin;
/***
* 徽章文本边缘与徽章背景边缘间的距离
*/
private int mBadgePadding;
/**
* 徽章文本
*/
private String mBadgeText;
/**
* 徽章文本所占区域大小
*/
private Rect mBadgeNumberRect;
/**
* 是否显示Badge
*/
private boolean mIsShowBadge;
/**
* 徽章在宿主控件中的位置
*/
private BadgeGravity mBadgeGravity;
/**
* 整个徽章所占区域
*/
private RectF mBadgeRectF;
/**
* 是否可拖动
*/
private boolean mDragable;
/**
* 拖拽徽章超出轨迹范围后,再次放回到轨迹范围时,是否恢复轨迹
*/
private boolean mIsResumeTravel;
/***
* 徽章描边宽度
*/
private int mBadgeBorderWidth;
/***
* 徽章描边颜色
*/
private int mBadgeBorderColor;
/**
* 触发开始拖拽徽章事件的扩展触摸距离
*/
private int mDragExtra;
/**
* 整个徽章加上其触发开始拖拽区域所占区域
*/
private RectF mBadgeDragExtraRectF;
/**
* 拖动时的徽章控件
*/
private BGADragBadgeView mDropBadgeView;
/**
* 是否正在拖动
*/
private boolean mIsDraging;
/**
* 拖动大于BGABadgeViewHelper.mMoveHiddenThreshold后抬起手指徽章消失的代理
*/
private BGADragDismissDelegate mDelegage;
private boolean mIsShowDrawable = false;
public BGABadgeViewHelper(BGABadgeable badgeable, Context context, AttributeSet attrs, BadgeGravity defaultBadgeGravity) {
mBadgeable = badgeable;
initDefaultAttrs(context, defaultBadgeGravity);
initCustomAttrs(context, attrs);
afterInitDefaultAndCustomAttrs();
mDropBadgeView = new BGADragBadgeView(context, this);
}
private void initDefaultAttrs(Context context, BadgeGravity defaultBadgeGravity) {
mBadgeNumberRect = new Rect();
mBadgeRectF = new RectF();
mBadgeBgColor = Color.RED;
mBadgeTextColor = Color.WHITE;
mBadgeTextSize = BGABadgeViewUtil.sp2px(context, 10);
mBadgePaint = new Paint();
mBadgePaint.setAntiAlias(true);
mBadgePaint.setStyle(Paint.Style.FILL);
// 设置mBadgeText居中,保证mBadgeText长度为1时,文本也能居中
mBadgePaint.setTextAlign(Paint.Align.CENTER);
mBadgePadding = BGABadgeViewUtil.dp2px(context, 4);
mBadgeVerticalMargin = BGABadgeViewUtil.dp2px(context, 4);
mBadgeHorizontalMargin = BGABadgeViewUtil.dp2px(context, 4);
mBadgeGravity = defaultBadgeGravity;
mIsShowBadge = false;
mBadgeText = null;
mBitmap = null;
mIsDraging = false;
mDragable = false;
mBadgeBorderColor = Color.WHITE;
mDragExtra = BGABadgeViewUtil.dp2px(context, 4);
mBadgeDragExtraRectF = new RectF();
}
private void initCustomAttrs(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BGABadgeView);
final int N = typedArray.getIndexCount();
for (int i = 0; i < N; i++) {
initCustomAttr(typedArray.getIndex(i), typedArray);
}
typedArray.recycle();
}
private void initCustomAttr(int attr, TypedArray typedArray) {
if (attr == R.styleable.BGABadgeView_badge_bgColor) {
mBadgeBgColor = typedArray.getColor(attr, mBadgeBgColor);
} else if (attr == R.styleable.BGABadgeView_badge_textColor) {
mBadgeTextColor = typedArray.getColor(attr, mBadgeTextColor);
} else if (attr == R.styleable.BGABadgeView_badge_textSize) {
mBadgeTextSize = typedArray.getDimensionPixelSize(attr, mBadgeTextSize);
} else if (attr == R.styleable.BGABadgeView_badge_verticalMargin) {
mBadgeVerticalMargin = typedArray.getDimensionPixelSize(attr, mBadgeVerticalMargin);
} else if (attr == R.styleable.BGABadgeView_badge_horizontalMargin) {
mBadgeHorizontalMargin = typedArray.getDimensionPixelSize(attr, mBadgeHorizontalMargin);
} else if (attr == R.styleable.BGABadgeView_badge_padding) {
mBadgePadding = typedArray.getDimensionPixelSize(attr, mBadgePadding);
} else if (attr == R.styleable.BGABadgeView_badge_gravity) {
int ordinal = typedArray.getInt(attr, mBadgeGravity.ordinal());
mBadgeGravity = BadgeGravity.values()[ordinal];
} else if (attr == R.styleable.BGABadgeView_badge_dragable) {
mDragable = typedArray.getBoolean(attr, mDragable);
} else if (attr == R.styleable.BGABadgeView_badge_isResumeTravel) {
mIsResumeTravel = typedArray.getBoolean(attr, mIsResumeTravel);
} else if (attr == R.styleable.BGABadgeView_badge_borderWidth) {
mBadgeBorderWidth = typedArray.getDimensionPixelSize(attr, mBadgeBorderWidth);
} else if (attr == R.styleable.BGABadgeView_badge_borderColor) {
mBadgeBorderColor = typedArray.getColor(attr, mBadgeBorderColor);
} else if (attr == R.styleable.BGABadgeView_badge_dragExtra) {
mDragExtra = typedArray.getDimensionPixelSize(attr, mDragExtra);
}
}
private void afterInitDefaultAndCustomAttrs() {
mBadgePaint.setTextSize(mBadgeTextSize);
}
public void setBadgeBgColorInt(int badgeBgColor) {
mBadgeBgColor = badgeBgColor;
mBadgeable.postInvalidate();
}
public void setBadgeTextColorInt(int badgeTextColor) {
mBadgeTextColor = badgeTextColor;
mBadgeable.postInvalidate();
}
public void setBadgeTextSizeSp(int badgetextSize) {
if (badgetextSize >= 0) {
mBadgeTextSize = BGABadgeViewUtil.sp2px(mBadgeable.getContext(), badgetextSize);
mBadgePaint.setTextSize(mBadgeTextSize);
mBadgeable.postInvalidate();
}
}
public void setBadgeVerticalMarginDp(int badgeVerticalMargin) {
if (badgeVerticalMargin >= 0) {
mBadgeVerticalMargin = BGABadgeViewUtil.dp2px(mBadgeable.getContext(), badgeVerticalMargin);
mBadgeable.postInvalidate();
}
}
public void setBadgeHorizontalMarginDp(int badgeHorizontalMargin) {
if (badgeHorizontalMargin >= 0) {
mBadgeHorizontalMargin = BGABadgeViewUtil.dp2px(mBadgeable.getContext(), badgeHorizontalMargin);
mBadgeable.postInvalidate();
}
}
public void setBadgePaddingDp(int badgePadding) {
if (badgePadding >= 0) {
mBadgePadding = BGABadgeViewUtil.dp2px(mBadgeable.getContext(), badgePadding);
mBadgeable.postInvalidate();
}
}
public void setBadgeGravity(BadgeGravity badgeGravity) {
if (badgeGravity != null) {
mBadgeGravity = badgeGravity;
mBadgeable.postInvalidate();
}
}
public void setDragable(boolean dragable) {
mDragable = dragable;
mBadgeable.postInvalidate();
}
public void setIsResumeTravel(boolean isResumeTravel) {
mIsResumeTravel = isResumeTravel;
mBadgeable.postInvalidate();
}
public void setBadgeBorderWidthDp(int badgeBorderWidthDp) {
if (badgeBorderWidthDp >= 0) {
mBadgeBorderWidth = BGABadgeViewUtil.dp2px(mBadgeable.getContext(), badgeBorderWidthDp);
mBadgeable.postInvalidate();
}
}
public void setBadgeBorderColorInt(int badgeBorderColor) {
mBadgeBorderColor = badgeBorderColor;
mBadgeable.postInvalidate();
}
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mBadgeDragExtraRectF.left = mBadgeRectF.left - mDragExtra;
mBadgeDragExtraRectF.top = mBadgeRectF.top - mDragExtra;
mBadgeDragExtraRectF.right = mBadgeRectF.right + mDragExtra;
mBadgeDragExtraRectF.bottom = mBadgeRectF.bottom + mDragExtra;
if ((mBadgeBorderWidth == 0 || mIsShowDrawable) && mDragable && mIsShowBadge && mBadgeDragExtraRectF.contains(event.getX(), event.getY())) {
mIsDraging = true;
mBadgeable.getParent().requestDisallowInterceptTouchEvent(true);
Rect badgeableRect = new Rect();
mBadgeable.getGlobalVisibleRect(badgeableRect);
mDropBadgeView.setStickCenter(badgeableRect.left + mBadgeRectF.left + mBadgeRectF.width() / 2, badgeableRect.top + mBadgeRectF.top + mBadgeRectF.height() / 2);
mDropBadgeView.onTouchEvent(event);
mBadgeable.postInvalidate();
return true;
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsDraging) {
mDropBadgeView.onTouchEvent(event);
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mIsDraging) {
mDropBadgeView.onTouchEvent(event);
mIsDraging = false;
return true;
}
break;
default:
break;
}
return mBadgeable.callSuperOnTouchEvent(event);
}
public void endDragWithDismiss() {
hiddenBadge();
if (mDelegage != null) {
mDelegage.onDismiss(mBadgeable);
}
}
public void endDragWithoutDismiss() {
mBadgeable.postInvalidate();
}
public void drawBadge(Canvas canvas) {
if (mIsShowBadge && !mIsDraging) {
if (mIsShowDrawable) {
drawDrawableBadge(canvas);
} else {
drawTextBadge(canvas);
}
}
}
/**
* 绘制图像徽章
*
* @param canvas
*/
private void drawDrawableBadge(Canvas canvas) {
mBadgeRectF.left = mBadgeable.getWidth() - mBadgeHorizontalMargin - mBitmap.getWidth();
mBadgeRectF.top = mBadgeVerticalMargin;
switch (mBadgeGravity) {
case RightTop:
mBadgeRectF.top = mBadgeVerticalMargin;
break;
case RightCenter:
mBadgeRectF.top = (mBadgeable.getHeight() - mBitmap.getHeight()) / 2;
break;
case RightBottom:
mBadgeRectF.top = mBadgeable.getHeight() - mBitmap.getHeight() - mBadgeVerticalMargin;
break;
default:
break;
}
canvas.drawBitmap(mBitmap, mBadgeRectF.left, mBadgeRectF.top, mBadgePaint);
mBadgeRectF.right = mBadgeRectF.left + mBitmap.getWidth();
mBadgeRectF.bottom = mBadgeRectF.top + mBitmap.getHeight();
}
/**
* 绘制文字徽章
*
* @param canvas
*/
private void drawTextBadge(Canvas canvas) {
String badgeText = "";
if (!TextUtils.isEmpty(mBadgeText)) {
badgeText = mBadgeText;
}
// 获取文本宽所占宽高
mBadgePaint.getTextBounds(badgeText, 0, badgeText.length(), mBadgeNumberRect);
// 计算徽章背景的宽高
int badgeHeight = mBadgeNumberRect.height() + mBadgePadding * 2;
int badgeWidth;
// 当mBadgeText的长度为1或0时,计算出来的高度会比宽度大,此时设置宽度等于高度
if (badgeText.length() == 1 || badgeText.length() == 0) {
badgeWidth = badgeHeight;
} else {
badgeWidth = mBadgeNumberRect.width() + mBadgePadding * 2;
}
// 计算徽章背景上下的值
mBadgeRectF.top = mBadgeVerticalMargin;
mBadgeRectF.bottom = mBadgeable.getHeight() - mBadgeVerticalMargin;
switch (mBadgeGravity) {
case RightTop:
mBadgeRectF.bottom = mBadgeRectF.top + badgeHeight;
break;
case RightCenter:
mBadgeRectF.top = (mBadgeable.getHeight() - badgeHeight) / 2;
mBadgeRectF.bottom = mBadgeRectF.top + badgeHeight;
break;
case RightBottom:
mBadgeRectF.top = mBadgeRectF.bottom - badgeHeight;
break;
default:
break;
}
// 计算徽章背景左右的值
mBadgeRectF.right = mBadgeable.getWidth() - mBadgeHorizontalMargin;
mBadgeRectF.left = mBadgeRectF.right - badgeWidth;
if (mBadgeBorderWidth > 0) {
// 设置徽章边框景色
mBadgePaint.setColor(mBadgeBorderColor);
// 绘制徽章边框背景
canvas.drawRoundRect(mBadgeRectF, badgeHeight / 2, badgeHeight / 2, mBadgePaint);
// 设置徽章背景色
mBadgePaint.setColor(mBadgeBgColor);
// 绘制徽章背景
canvas.drawRoundRect(new RectF(mBadgeRectF.left + mBadgeBorderWidth, mBadgeRectF.top + mBadgeBorderWidth, mBadgeRectF.right - mBadgeBorderWidth, mBadgeRectF.bottom - mBadgeBorderWidth), (badgeHeight - 2 * mBadgeBorderWidth) / 2, (badgeHeight - 2 * mBadgeBorderWidth) / 2, mBadgePaint);
} else {
// 设置徽章背景色
mBadgePaint.setColor(mBadgeBgColor);
// 绘制徽章背景
canvas.drawRoundRect(mBadgeRectF, badgeHeight / 2, badgeHeight / 2, mBadgePaint);
}
if (!TextUtils.isEmpty(mBadgeText)) {
// 设置徽章文本颜色
mBadgePaint.setColor(mBadgeTextColor);
// initDefaultAttrs方法中设置了mBadgeText居中,此处的x为徽章背景的中心点y
float x = mBadgeRectF.left + badgeWidth / 2;
// 注意:绘制文本时的y是指文本底部,而不是文本的中间
float y = mBadgeRectF.bottom - mBadgePadding;
// 绘制徽章文本
canvas.drawText(badgeText, x, y, mBadgePaint);
}
}
public void showCirclePointBadge() {
showTextBadge(null);
}
public void showTextBadge(String badgeText) {
mIsShowDrawable = false;
mBadgeText = badgeText;
mIsShowBadge = true;
mBadgeable.postInvalidate();
}
public void hiddenBadge() {
mIsShowBadge = false;
mBadgeable.postInvalidate();
}
public boolean isShowBadge() {
return mIsShowBadge;
}
public void showDrawable(Bitmap bitmap) {
mBitmap = bitmap;
mIsShowDrawable = true;
mIsShowBadge = true;
mBadgeable.postInvalidate();
}
public boolean isShowDrawable() {
return mIsShowDrawable;
}
public RectF getBadgeRectF() {
return mBadgeRectF;
}
public int getBadgePadding() {
return mBadgePadding;
}
public String getBadgeText() {
return mBadgeText;
}
public int getBadgeBgColor() {
return mBadgeBgColor;
}
public int getBadgeTextColor() {
return mBadgeTextColor;
}
public int getBadgeTextSize() {
return mBadgeTextSize;
}
public Bitmap getBitmap() {
return mBitmap;
}
public void setDragDismissDelegage(BGADragDismissDelegate delegage) {
mDelegage = delegage;
}
public View getRootView() {
return mBadgeable.getRootView();
}
public boolean isResumeTravel() {
return mIsResumeTravel;
}
public enum BadgeGravity {
RightTop,
RightCenter,
RightBottom
}
}
@@ -0,0 +1,100 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.TypedValue;
import android.view.View;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/12/5 上午12:27
* 描述:
*/
public class BGABadgeViewUtil {
private BGABadgeViewUtil() {
}
public static int dp2px(Context context, float dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
}
public static int sp2px(Context context, float spValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics());
}
public static int getStatusBarHeight(View view) {
Rect rectangle = new Rect();
view.getRootView().getWindowVisibleDisplayFrame(rectangle);
return rectangle.top;
}
public static Bitmap createBitmapSafely(BGADragBadgeView dragBadgeView, Rect rect, int retryCount) {
try {
dragBadgeView.setDrawingCacheEnabled(true);
// 只裁剪徽章区域,不然会很卡
return Bitmap.createBitmap(dragBadgeView.getDrawingCache(), rect.left < 0 ? 0 : rect.left, rect.top < 0 ? 0 : rect.top, rect.width(), rect.height());
} catch (OutOfMemoryError e) {
if (retryCount > 0) {
System.gc();
return createBitmapSafely(dragBadgeView, rect, retryCount - 1);
}
return null;
}
}
public static float getDistanceBetween2Points(PointF p0, PointF p1) {
float distance = (float) Math.sqrt(Math.pow(p0.y - p1.y, 2) + Math.pow(p0.x - p1.x, 2));
return distance;
}
public static PointF getMiddlePoint(PointF p1, PointF p2) {
return new PointF((p1.x + p2.x) / 2.0f, (p1.y + p2.y) / 2.0f);
}
public static PointF getPointByPercent(PointF p1, PointF p2, float percent) {
return new PointF(evaluate(percent, p1.x, p2.x), evaluate(percent, p1.y, p2.y));
}
// 从FloatEvaluator中拷贝过来,这样就不用每次都new FloatEvaluator了
public static Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
public static PointF[] getIntersectionPoints(PointF pMiddle, float radius, Double lineK) {
PointF[] points = new PointF[2];
float radian, xOffset = 0, yOffset = 0;
if (lineK != null) {
radian = (float) Math.atan(lineK);
xOffset = (float) (Math.sin(radian) * radius);
yOffset = (float) (Math.cos(radian) * radius);
} else {
xOffset = radius;
yOffset = 0;
}
points[0] = new PointF(pMiddle.x + xOffset, pMiddle.y - yOffset);
points[1] = new PointF(pMiddle.x - xOffset, pMiddle.y + yOffset);
return points;
}
}
@@ -0,0 +1,95 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/6 下午10:42
* 描述:
*/
public interface BGABadgeable {
/**
* 显示圆点徽章
*/
void showCirclePointBadge();
/**
* 显示文字徽章
*
* @param badgeText
*/
void showTextBadge(String badgeText);
/**
* 隐藏徽章
*/
void hiddenBadge();
/**
* 显示图像徽章
*
* @param bitmap
*/
void showDrawableBadge(Bitmap bitmap);
/**
* 调用父类的onTouchEvent方法
*
* @param event
* @return
*/
boolean callSuperOnTouchEvent(MotionEvent event);
/**
* 拖动大于BGABadgeViewHelper.mMoveHiddenThreshold后抬起手指徽章消失的代理
*
* @param delegate
*/
void setDragDismissDelegage(BGADragDismissDelegate delegate);
/**
* 是否显示徽章
*
* @return
*/
boolean isShowBadge();
BGABadgeViewHelper getBadgeViewHelper();
int getWidth();
int getHeight();
void postInvalidate();
ViewParent getParent();
int getId();
boolean getGlobalVisibleRect(Rect r);
Context getContext();
View getRootView();
}
@@ -0,0 +1,478 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.OvershootInterpolator;
import java.lang.ref.WeakReference;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/12 下午3:23
* 描述:
*/
class BGADragBadgeView extends View {
private static final String TAG = BGADragBadgeView.class.getSimpleName();
private BGABadgeViewHelper mBadgeViewHelper;
private Paint mBadgePaint;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mLayoutParams;
private int mStartX;
private int mStartY;
private BGAExplosionAnimator mExplosionAnimator;
private SetExplosionAnimatorNullTask mSetExplosionAnimatorNullTask;
/**
* 针圆切线的切点
*/
private PointF[] mStickPoints = new PointF[]{
new PointF(0, 0),
new PointF(0, 0)
};
/**
* 拖拽圆切线的切点
*/
private PointF[] mDragPoints = new PointF[]{
new PointF(0, 0),
new PointF(0, 0)
};
/**
* 控制点
*/
private PointF mControlPoint = new PointF(0, 0);
/**
* 拖拽圆中心点
*/
private PointF mDragCenter = new PointF(0, 0);
/**
* 拖拽圆半径
*/
private float mDragRadius;
/**
* 针圆中心点
*/
private PointF mStickCenter;
/**
* 针圆半径
*/
private float mStickRadius;
/**
* 拖拽圆最大半径
*/
private int mMaxDragRadius;
/**
* 拖拽圆半径和针圆半径的差值
*/
private int mDragStickRadiusDifference;
/**
* 拖动mDismissThreshold距离后抬起手指徽章消失
*/
private int mDismissThreshold;
private boolean mDismissAble;
private boolean mIsDragDisappear;
public BGADragBadgeView(Context context, BGABadgeViewHelper badgeViewHelper) {
super(context);
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mBadgeViewHelper = badgeViewHelper;
initBadgePaint();
initLayoutParams();
initStick();
mSetExplosionAnimatorNullTask = new SetExplosionAnimatorNullTask(this);
}
private void initBadgePaint() {
mBadgePaint = new Paint();
mBadgePaint.setAntiAlias(true);
mBadgePaint.setStyle(Paint.Style.FILL);
// 设置mBadgeText居中,保证mBadgeText长度为1时,文本也能居中
mBadgePaint.setTextAlign(Paint.Align.CENTER);
mBadgePaint.setTextSize(mBadgeViewHelper.getBadgeTextSize());
}
private void initLayoutParams() {
mLayoutParams = new WindowManager.LayoutParams();
mLayoutParams.gravity = Gravity.LEFT + Gravity.TOP;
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLayoutParams.format = PixelFormat.TRANSLUCENT;
mLayoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;
mLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
mLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
}
private void initStick() {
mMaxDragRadius = BGABadgeViewUtil.dp2px(getContext(), 10);
mDragStickRadiusDifference = BGABadgeViewUtil.dp2px(getContext(), 1);
}
@Override
protected void onDraw(Canvas canvas) {
try {
if (mExplosionAnimator == null) {
if (mBadgeViewHelper.isShowDrawable()) {
if (mBadgeViewHelper.getBadgeBgColor() == Color.RED) {
mBadgePaint.setColor(mBadgeViewHelper.getBitmap().getPixel(mBadgeViewHelper.getBitmap().getWidth() / 2, mBadgeViewHelper.getBitmap().getHeight() / 2));
} else {
mBadgePaint.setColor(mBadgeViewHelper.getBadgeBgColor());
}
drawStick(canvas);
drawDrawableBadge(canvas);
} else {
mBadgePaint.setColor(mBadgeViewHelper.getBadgeBgColor());
drawStick(canvas);
drawTextBadge(canvas);
}
} else {
mExplosionAnimator.draw(canvas);
}
} catch (Exception e) {
// 确保自己能被移除
removeSelfWithException();
}
}
private void drawDrawableBadge(Canvas canvas) {
canvas.drawBitmap(mBadgeViewHelper.getBitmap(), mStartX, mStartY, mBadgePaint);
}
private void drawTextBadge(Canvas canvas) {
// 设置徽章背景色
mBadgePaint.setColor(mBadgeViewHelper.getBadgeBgColor());
// 绘制徽章背景
canvas.drawRoundRect(new RectF(mStartX, mStartY, mStartX + mBadgeViewHelper.getBadgeRectF().width(), mStartY + mBadgeViewHelper.getBadgeRectF().height()), mBadgeViewHelper.getBadgeRectF().height() / 2, mBadgeViewHelper.getBadgeRectF().height() / 2, mBadgePaint);
// 设置徽章文本颜色
mBadgePaint.setColor(mBadgeViewHelper.getBadgeTextColor());
float x = mStartX + mBadgeViewHelper.getBadgeRectF().width() / 2;
// 注意:绘制文本时的y是指文本底部,而不是文本的中间
float y = mStartY + mBadgeViewHelper.getBadgeRectF().height() - mBadgeViewHelper.getBadgePadding();
// 绘制徽章文本
String badgeText = mBadgeViewHelper.getBadgeText() == null ? "" : mBadgeViewHelper.getBadgeText();
canvas.drawText(badgeText, x, y, mBadgePaint);
}
private void drawStick(Canvas canvas) {
float currentStickRadius = getCurrentStickRadius();
// 2. 获取直线与圆的交点
float yOffset = mStickCenter.y - mDragCenter.y;
float xOffset = mStickCenter.x - mDragCenter.x;
Double lineK = null;
if (xOffset != 0) {
lineK = (double) (yOffset / xOffset);
}
// 通过几何图形工具获取交点坐标
mDragPoints = BGABadgeViewUtil.getIntersectionPoints(mDragCenter, mDragRadius, lineK);
mStickPoints = BGABadgeViewUtil.getIntersectionPoints(mStickCenter, currentStickRadius, lineK);
// 3. 获取控制点坐标
mControlPoint = BGABadgeViewUtil.getMiddlePoint(mDragCenter, mStickCenter);
// 保存画布状态
canvas.save();
canvas.translate(0, -BGABadgeViewUtil.getStatusBarHeight(mBadgeViewHelper.getRootView()));
if (!mIsDragDisappear) {
if (!mDismissAble) {
// 3. 画连接部分
Path path = new Path();
// 跳到点1
path.moveTo(mStickPoints[0].x, mStickPoints[0].y);
// 画曲线1 -> 2
path.quadTo(mControlPoint.x, mControlPoint.y, mDragPoints[0].x, mDragPoints[0].y);
// 画直线2 -> 3
path.lineTo(mDragPoints[1].x, mDragPoints[1].y);
// 画曲线3 -> 4
path.quadTo(mControlPoint.x, mControlPoint.y, mStickPoints[1].x, mStickPoints[1].y);
path.close();
canvas.drawPath(path, mBadgePaint);
// 2. 画固定圆
canvas.drawCircle(mStickCenter.x, mStickCenter.y, currentStickRadius, mBadgePaint);
}
// 1. 画拖拽圆
canvas.drawCircle(mDragCenter.x, mDragCenter.y, mDragRadius, mBadgePaint);
}
// 恢复上次的保存状态
canvas.restore();
}
/**
* 获取针圆实时半径
*
* @return
*/
private float getCurrentStickRadius() {
/**
* distance 0 -> mDismissThreshold
* percent 0.0f -> 1.0f
* currentStickRadius mStickRadius * 100% -> mStickRadius * 20%
*/
float distance = BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter);
distance = Math.min(distance, mDismissThreshold);
float percent = distance / mDismissThreshold;
return BGABadgeViewUtil.evaluate(percent, mStickRadius, mStickRadius * 0.2f);
}
public void setStickCenter(float x, float y) {
mStickCenter = new PointF(x, y);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
handleActionDown(event);
break;
case MotionEvent.ACTION_MOVE:
handleActionMove(event);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
handleActionUp(event);
break;
}
} catch (Exception e) {
// 确保自己能被移除
removeSelfWithException();
}
return true;
}
private void handleActionDown(MotionEvent event) {
if (mExplosionAnimator == null && getParent() == null) {
mDragRadius = Math.min(mBadgeViewHelper.getBadgeRectF().width() / 2, mMaxDragRadius);
mStickRadius = mDragRadius - mDragStickRadiusDifference;
mDismissThreshold = (int) (mStickRadius * 10);
mDismissAble = false;
mIsDragDisappear = false;
mWindowManager.addView(this, mLayoutParams);
updateDragPosition(event.getRawX(), event.getRawY());
}
}
private void handleActionMove(MotionEvent event) {
if (mExplosionAnimator == null && getParent() != null) {
updateDragPosition(event.getRawX(), event.getRawY());
// 处理断开事件
if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) {
mDismissAble = true;
postInvalidate();
} else if (mBadgeViewHelper.isResumeTravel()) {
mDismissAble = false;
postInvalidate();
}
}
}
private void handleActionUp(MotionEvent event) {
handleActionMove(event);
if (mDismissAble) {
// 拖拽点超出过范围
if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) {
// 现在也超出范围,消失
try {
mIsDragDisappear = true;
startDismissAnim(getNewStartX(event.getRawX()), getNewStartY(event.getRawY()));
} catch (Exception e) {
removeSelf();
mBadgeViewHelper.endDragWithDismiss();
}
} else {
// 现在没有超出范围,放回去
removeSelf();
mBadgeViewHelper.endDragWithoutDismiss();
}
} else {
// 拖拽点没超出过范围,弹回去
try {
startSpringAnim();
} catch (Exception e) {
removeSelf();
mBadgeViewHelper.endDragWithoutDismiss();
}
}
}
private void startSpringAnim() {
final PointF startReleaseDragCenter = new PointF(mDragCenter.x, mDragCenter.y);
ValueAnimator springAnim = ValueAnimator.ofFloat(1.0f);
springAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator mAnim) {
// 0.0 -> 1.0f
float percent = mAnim.getAnimatedFraction();
PointF p = BGABadgeViewUtil.getPointByPercent(startReleaseDragCenter, mStickCenter, percent);
updateDragPosition(p.x, p.y);
}
});
springAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
removeSelf();
mBadgeViewHelper.endDragWithoutDismiss();
}
@Override
public void onAnimationCancel(Animator animation) {
removeSelf();
mBadgeViewHelper.endDragWithoutDismiss();
}
});
springAnim.setInterpolator(new OvershootInterpolator(4));
springAnim.setRepeatCount(1);
springAnim.setRepeatMode(ValueAnimator.INFINITE);
springAnim.setDuration(BGAExplosionAnimator.ANIM_DURATION / 2);
springAnim.start();
}
private void startDismissAnim(int newX, int newY) {
int badgeWidth = (int) mBadgeViewHelper.getBadgeRectF().width();
int badgeHeight = (int) mBadgeViewHelper.getBadgeRectF().height();
Rect rect = new Rect(newX - badgeWidth / 2, newY - badgeHeight / 2, newX + badgeWidth / 2, newY + badgeHeight / 2);
Bitmap badgeBitmap = BGABadgeViewUtil.createBitmapSafely(this, rect, 1);
if (badgeBitmap == null) {
removeSelf();
mBadgeViewHelper.endDragWithDismiss();
return;
}
if (mExplosionAnimator != null) {
removeSelf();
mBadgeViewHelper.endDragWithDismiss();
return;
}
mExplosionAnimator = new BGAExplosionAnimator(this, rect, badgeBitmap);
mExplosionAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
removeSelf();
mBadgeViewHelper.endDragWithDismiss();
}
@Override
public void onAnimationCancel(Animator animation) {
removeSelf();
mBadgeViewHelper.endDragWithDismiss();
}
});
mExplosionAnimator.start();
}
private void removeSelf() {
if (getParent() != null) {
mWindowManager.removeView(this);
}
mDismissAble = false;
mIsDragDisappear = false;
// 处理有时候爆炸效果结束后出现一瞬间的拖拽效果
postDelayed(mSetExplosionAnimatorNullTask, 60);
}
/**
* 修改拖拽位置
*
* @param rawX
* @param rawY
*/
private void updateDragPosition(float rawX, float rawY) {
mStartX = getNewStartX(rawX);
mStartY = getNewStartY(rawY);
mDragCenter.set(rawX, rawY);
postInvalidate();
}
private int getNewStartX(float rawX) {
int badgeWidth = (int) mBadgeViewHelper.getBadgeRectF().width();
int newX = (int) rawX - badgeWidth / 2;
if (newX < 0) {
newX = 0;
}
if (newX > mWindowManager.getDefaultDisplay().getWidth() - badgeWidth) {
newX = mWindowManager.getDefaultDisplay().getWidth() - badgeWidth;
}
return newX;
}
private int getNewStartY(float rawY) {
int badgeHeight = (int) mBadgeViewHelper.getBadgeRectF().height();
int maxNewY = getHeight() - badgeHeight;
int newStartY = (int) rawY - badgeHeight / 2 - BGABadgeViewUtil.getStatusBarHeight(mBadgeViewHelper.getRootView());
return Math.min(Math.max(0, newStartY), maxNewY);
}
private void removeSelfWithException() {
removeSelf();
if (BGABadgeViewUtil.getDistanceBetween2Points(mDragCenter, mStickCenter) > mDismissThreshold) {
mBadgeViewHelper.endDragWithDismiss();
} else {
mBadgeViewHelper.endDragWithoutDismiss();
}
}
private static class SetExplosionAnimatorNullTask implements Runnable {
private final WeakReference<BGADragBadgeView> mDragBadgeView;
public SetExplosionAnimatorNullTask(BGADragBadgeView dragBadgeView) {
mDragBadgeView = new WeakReference<>(dragBadgeView);
}
@Override
public void run() {
BGADragBadgeView dragBadgeView = mDragBadgeView.get();
if (dragBadgeView != null) {
dragBadgeView.mExplosionAnimator = null;
}
}
}
}
@@ -0,0 +1,32 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/7/12 下午6:33
* 描述:拖动大于BGABadgeViewHelper.mMoveHiddenThreshold后抬起手指徽章消失的代理
*/
public interface BGADragDismissDelegate {
/**
* 拖动大于BGABadgeViewHelper.mMoveHiddenThreshold后抬起手指徽章消失的回调方法
*
* @param badgeable
*/
void onDismiss(BGABadgeable badgeable);
}
@@ -0,0 +1,170 @@
/**
* Copyright 2015 bingoogolapple
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.bingoogolapple.badgeview;
import android.animation.ValueAnimator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Interpolator;
import java.util.Random;
/**
* 作者:王浩 邮件:bingoogolapple@gmail.com
* 创建时间:15/12/5 上午12:21
* 描述:参考https://github.com/tyrantgit/ExplosionField改成了只有一个View的情况,只刷新徽章附近的区域
*/
public class BGAExplosionAnimator extends ValueAnimator {
public static final int ANIM_DURATION = 300;
private static final Interpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator(0.6f);
private static final float END_VALUE = 1.4f;
private static final int REFRESH_RATIO = 3;
private static float X;
private static float Y;
private static float V;
private static float W;
private Particle[] mParticles;
private Paint mPaint;
private BGADragBadgeView mDragBadgeView;
private Rect mRect;
private Rect mInvalidateRect;
public BGAExplosionAnimator(BGADragBadgeView dragBadgeView, Rect rect, Bitmap bitmap) {
setFloatValues(0.0f, END_VALUE);
setDuration(ANIM_DURATION);
setInterpolator(DEFAULT_INTERPOLATOR);
X = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 5);
Y = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 20);
V = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 2);
W = BGABadgeViewUtil.dp2px(dragBadgeView.getContext(), 1);
mPaint = new Paint();
mDragBadgeView = dragBadgeView;
mRect = rect;
mInvalidateRect = new Rect(mRect.left - mRect.width() * REFRESH_RATIO, mRect.top - mRect.height() * REFRESH_RATIO, mRect.right + mRect.width() * REFRESH_RATIO, mRect.bottom + mRect.height() * REFRESH_RATIO);
int partLen = 15;
mParticles = new Particle[partLen * partLen];
Random random = new Random(System.currentTimeMillis());
int w = bitmap.getWidth() / (partLen + 2);
int h = bitmap.getHeight() / (partLen + 2);
for (int i = 0; i < partLen; i++) {
for (int j = 0; j < partLen; j++) {
mParticles[(i * partLen) + j] = generateParticle(bitmap.getPixel((j + 1) * w, (i + 1) * h), random);
}
}
}
private Particle generateParticle(int color, Random random) {
Particle particle = new Particle();
particle.color = color;
particle.radius = V;
if (random.nextFloat() < 0.2f) {
particle.baseRadius = V + ((X - V) * random.nextFloat());
} else {
particle.baseRadius = W + ((V - W) * random.nextFloat());
}
float nextFloat = random.nextFloat();
particle.top = mRect.height() * ((0.18f * random.nextFloat()) + 0.2f);
particle.top = nextFloat < 0.2f ? particle.top : particle.top + ((particle.top * 0.2f) * random.nextFloat());
particle.bottom = (mRect.height() * (random.nextFloat() - 0.5f)) * 1.8f;
float f = nextFloat < 0.2f ? particle.bottom : nextFloat < 0.8f ? particle.bottom * 0.6f : particle.bottom * 0.3f;
particle.bottom = f;
particle.mag = 4.0f * particle.top / particle.bottom;
particle.neg = (-particle.mag) / particle.bottom;
f = mRect.centerX() + (Y * (random.nextFloat() - 0.5f));
particle.baseCx = f + mRect.width() / 2;
particle.cx = particle.baseCx;
f = mRect.centerY() + (Y * (random.nextFloat() - 0.5f));
particle.baseCy = f;
particle.cy = f;
particle.life = END_VALUE / 10 * random.nextFloat();
particle.overflow = 0.4f * random.nextFloat();
particle.alpha = 1f;
return particle;
}
public void draw(Canvas canvas) {
if (!isStarted()) {
return;
}
for (Particle particle : mParticles) {
particle.advance((float) getAnimatedValue());
if (particle.alpha > 0f) {
mPaint.setColor(particle.color);
mPaint.setAlpha((int) (Color.alpha(particle.color) * particle.alpha));
canvas.drawCircle(particle.cx, particle.cy, particle.radius, mPaint);
}
}
postInvalidate();
}
@Override
public void start() {
super.start();
postInvalidate();
}
/**
* 只刷新徽章附近的区域
*/
private void postInvalidate() {
mDragBadgeView.postInvalidate(mInvalidateRect.left, mInvalidateRect.top, mInvalidateRect.right, mInvalidateRect.bottom);
}
private class Particle {
float alpha;
int color;
float cx;
float cy;
float radius;
float baseCx;
float baseCy;
float baseRadius;
float top;
float bottom;
float mag;
float neg;
float life;
float overflow;
public void advance(float factor) {
float f = 0f;
float normalization = factor / END_VALUE;
if (normalization < life || normalization > 1f - overflow) {
alpha = 0f;
return;
}
normalization = (normalization - life) / (1f - life - overflow);
float f2 = normalization * END_VALUE;
if (normalization >= 0.7f) {
f = (normalization - 0.7f) / 0.3f;
}
alpha = 1f - f;
f = bottom * f2;
cx = baseCx + f;
cy = (float) (baseCy - this.neg * Math.pow(f, 2.0)) - f * mag;
radius = V + (baseRadius - V) * f2;
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="BGABadgeView">
<!-- 徽章背景色 -->
<attr name="badge_bgColor" format="reference|color" />
<!-- 徽章文本的颜色 -->
<attr name="badge_textColor" format="reference|color" />
<!-- 徽章文本字体大小 -->
<attr name="badge_textSize" format="reference|dimension" />
<!-- 徽章背景与宿主控件上下边缘间距离 -->
<attr name="badge_verticalMargin" format="reference|dimension" />
<!-- 徽章背景与宿主控件左右边缘间距离 -->
<attr name="badge_horizontalMargin" format="reference|dimension" />
<!-- 徽章文本边缘与徽章背景边缘间的距离 -->
<attr name="badge_padding" format="reference|dimension" />
<!-- 徽章在宿主控件中的位置 -->
<attr name="badge_gravity" format="enum">
<enum name="rightTop" value="0" />
<enum name="rightCenter" value="1" />
<enum name="rightBottom" value="2" />
</attr>
<!-- 是否可以拖拽删除徽章 -->
<attr name="badge_dragable" format="boolean" />
<!-- 拖拽徽章超出轨迹范围后,再次放回到轨迹范围时,是否恢复轨迹 -->
<attr name="badge_isResumeTravel" format="boolean" />
<!-- 徽章描边宽度 -->
<attr name="badge_borderWidth" format="reference|dimension" />
<!-- 徽章描边颜色 -->
<attr name="badge_borderColor" format="reference|color" />
<!-- 触发开始拖拽徽章事件的扩展触摸距离-->
<attr name="badge_dragExtra" format="reference|dimension" />
</declare-styleable>
</resources>