Skip to content

Commit f2abff1

Browse files
VSpotView2
1 parent 31a7e69 commit f2abff1

2 files changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package com.developer.spoti.vspoti;
2+
3+
import android.app.Activity;
4+
import android.content.Context;
5+
import android.graphics.Bitmap;
6+
import android.graphics.Canvas;
7+
import android.graphics.Color;
8+
import android.graphics.Paint;
9+
import android.graphics.Point;
10+
import android.graphics.PorterDuff;
11+
import android.graphics.PorterDuffXfermode;
12+
import android.graphics.RectF;
13+
import android.graphics.Xfermode;
14+
import android.view.MotionEvent;
15+
import android.view.View;
16+
import android.view.ViewGroup;
17+
import android.widget.FrameLayout;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
public class VSpotView2 extends FrameLayout {
23+
24+
private static final float INDICATOR_HEIGHT = 30;
25+
private float density;
26+
private List<View> targetViews;
27+
private List<RectF> targetRects;
28+
private VSpotMessageView mMessageView;
29+
private boolean isTop;
30+
private Gravity mGravity;
31+
private DismissType dismissType;
32+
int marginGuide;
33+
private boolean mIsShowing;
34+
private VSpotListener mVSpotListener;
35+
private int currentTargetIndex = 0;
36+
37+
final int ANIMATION_DURATION = 400;
38+
final Paint emptyPaint = new Paint();
39+
final Paint paintLine = new Paint();
40+
final Paint paintCircle = new Paint();
41+
final Paint paintCircleInner = new Paint();
42+
final Paint mPaint = new Paint();
43+
final Paint targetPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
44+
final Xfermode XFERMODE_CLEAR = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
45+
46+
public interface VSpotListener {
47+
void onDismiss(View view);
48+
}
49+
50+
public enum Gravity {
51+
auto, center
52+
}
53+
54+
public enum DismissType {
55+
outside, anywhere, targetView
56+
}
57+
58+
private VSpotView2(Context context, List<View> views) {
59+
super(context);
60+
setWillNotDraw(false);
61+
62+
this.targetViews = views;
63+
this.targetRects = new ArrayList<>();
64+
65+
density = context.getResources().getDisplayMetrics().density;
66+
67+
for (View target : targetViews) {
68+
int[] locationTarget = new int[2];
69+
target.getLocationOnScreen(locationTarget);
70+
targetRects.add(new RectF(locationTarget[0], locationTarget[1],
71+
locationTarget[0] + target.getWidth(),
72+
locationTarget[1] + target.getHeight()));
73+
}
74+
75+
mMessageView = new VSpotMessageView(getContext());
76+
final int padding = (int) (5 * density);
77+
mMessageView.setPadding(padding, padding, padding, padding);
78+
mMessageView.setColor(Color.WHITE);
79+
80+
addView(mMessageView, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
81+
ViewGroup.LayoutParams.WRAP_CONTENT));
82+
83+
setMessageLocation(resolveMessageViewLocation(currentTargetIndex));
84+
}
85+
86+
@Override
87+
protected void onDraw(Canvas canvas) {
88+
super.onDraw(canvas);
89+
if (!targetRects.isEmpty()) {
90+
Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
91+
Canvas tempCanvas = new Canvas(bitmap);
92+
93+
mPaint.setColor(0xdd000000);
94+
mPaint.setStyle(Paint.Style.FILL);
95+
mPaint.setAntiAlias(true);
96+
tempCanvas.drawRect(canvas.getClipBounds(), mPaint);
97+
98+
targetPaint.setXfermode(XFERMODE_CLEAR);
99+
targetPaint.setAntiAlias(true);
100+
101+
for (RectF rect : targetRects) {
102+
tempCanvas.drawRoundRect(rect, 15, 15, targetPaint);
103+
}
104+
canvas.drawBitmap(bitmap, 0, 0, emptyPaint);
105+
}
106+
}
107+
108+
public void nextTarget() {
109+
if (currentTargetIndex < targetViews.size() - 1) {
110+
currentTargetIndex++;
111+
setMessageLocation(resolveMessageViewLocation(currentTargetIndex));
112+
invalidate();
113+
}
114+
}
115+
116+
public void previousTarget() {
117+
if (currentTargetIndex > 0) {
118+
currentTargetIndex--;
119+
setMessageLocation(resolveMessageViewLocation(currentTargetIndex));
120+
invalidate();
121+
}
122+
}
123+
124+
public boolean isShowing() {
125+
return mIsShowing;
126+
}
127+
128+
public void dismiss() {
129+
((ViewGroup) ((Activity) getContext()).getWindow().getDecorView()).removeView(this);
130+
mIsShowing = false;
131+
if (mVSpotListener != null) {
132+
mVSpotListener.onDismiss(targetViews.get(currentTargetIndex));
133+
}
134+
}
135+
136+
@Override
137+
public boolean onTouchEvent(MotionEvent event) {
138+
float x = event.getX();
139+
float y = event.getY();
140+
141+
if (event.getAction() == MotionEvent.ACTION_DOWN) {
142+
switch (dismissType) {
143+
case outside:
144+
if (!isViewContains(mMessageView, x, y)) dismiss();
145+
break;
146+
case anywhere:
147+
dismiss();
148+
break;
149+
case targetView:
150+
if (targetRects.get(currentTargetIndex).contains(x, y)) {
151+
targetViews.get(currentTargetIndex).performClick();
152+
dismiss();
153+
}
154+
break;
155+
}
156+
return true;
157+
}
158+
return false;
159+
}
160+
161+
private Point resolveMessageViewLocation(int index) {
162+
RectF rect = targetRects.get(index);
163+
int xMessageView = (int) (rect.right) - mMessageView.getWidth();
164+
int yMessageView = (int) (rect.top + targetViews.get(index).getHeight() + INDICATOR_HEIGHT * density);
165+
return new Point(xMessageView, yMessageView);
166+
}
167+
168+
void setMessageLocation(Point p) {
169+
mMessageView.setX(p.x);
170+
mMessageView.setY(p.y);
171+
requestLayout();
172+
}
173+
174+
public static class Builder {
175+
private List<View> targetViews = new ArrayList<>();
176+
private Context context;
177+
private VSpotListener vSpotListener;
178+
179+
public Builder(Context context) {
180+
this.context = context;
181+
}
182+
183+
public Builder addTargetView(View view) {
184+
this.targetViews.add(view);
185+
return this;
186+
}
187+
188+
public Builder setVSpotListener(VSpotListener listener) {
189+
this.vSpotListener = listener;
190+
return this;
191+
}
192+
193+
public VSpotView2 build() {
194+
VSpotView2 vSpotView = new VSpotView2(context, targetViews);
195+
vSpotView.mVSpotListener = vSpotListener;
196+
return vSpotView;
197+
}
198+
}
199+
}
200+
201+

sample/src/main/java/com/developer/spoti/vspot/MainActivity.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@
66
import androidx.appcompat.app.AppCompatActivity;
77

88
import com.developer.spoti.vspoti.VSpotView;
9+
import com.developer.spoti.vspoti.VSpotView2;
910

1011
public class MainActivity extends AppCompatActivity {
1112

1213
private VSpotView mVSpotView;
1314
private VSpotView.Builder builder;
15+
private VSpotView2 vSpotView2;
16+
private VSpotView2.Builder builder2;
1417

1518
@Override
1619
protected void onCreate(Bundle savedInstanceState) {
@@ -49,6 +52,14 @@ protected void onCreate(Bundle savedInstanceState) {
4952

5053
mVSpotView = builder.build();
5154
mVSpotView.show();
55+
56+
builder2 = new VSpotView2.Builder(MainActivity.this, view1)
57+
.setDismissType(VSpotView2.DismissType.anywhere)
58+
.setGravity(VSpotView2.Gravity.center);
59+
60+
vSpotView2 = builder2.build();
61+
vSpotView2.show();
62+
5263
}
5364

5465

0 commit comments

Comments
 (0)