11 | *
Email:1006368252@qq.com
12 | *
QQ:1006368252
13 | *
https://github.com/JustinRoom/Guidance
14 | *
15 | * @author jiangshicheng
16 | */
17 | public class BlankSpaceItemDecoration extends RecyclerView.ItemDecoration {
18 |
19 | int leftSpace;
20 | int topSpace;
21 | int rightSpace;
22 | int bottomSpace;
23 | boolean showFirstTop;
24 | boolean showLastBottom;
25 |
26 | public BlankSpaceItemDecoration(int leftSpace, int topSpace, int rightSpace, int bottomSpace) {
27 | this.leftSpace = leftSpace;
28 | this.topSpace = topSpace;
29 | this.rightSpace = rightSpace;
30 | this.bottomSpace = bottomSpace;
31 | }
32 |
33 | public void showFFTLB(boolean showFirstTop, boolean showLastBottom){
34 | this.showFirstTop = showFirstTop;
35 | this.showLastBottom = showLastBottom;
36 | }
37 |
38 | @Override
39 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
40 | super.getItemOffsets(outRect, view, parent, state);
41 | outRect.left = leftSpace;
42 | outRect.top = topSpace;
43 | outRect.right = rightSpace;
44 | outRect.bottom = bottomSpace;
45 | RecyclerView.Adapter adapter = parent.getAdapter();
46 | if (adapter != null && adapter.getItemCount() > 1) {
47 | if (!showFirstTop && parent.getChildAdapterPosition(view) == 0)
48 | outRect.top = 0;
49 |
50 | if (!showLastBottom && parent.getChildAdapterPosition(view) == adapter.getItemCount() - 1)
51 | outRect.bottom = 0;
52 | }
53 | }
54 |
55 | @Override
56 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
57 | super.onDraw(c, parent, state);
58 | }
59 |
60 | @Override
61 | public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
62 | super.onDrawOver(c, parent, state);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/jsc/exam/com/guidance/adapter/ClassItemAdapter.java:
--------------------------------------------------------------------------------
1 | package jsc.exam.com.guidance.adapter;
2 |
3 |
4 | import android.support.annotation.NonNull;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import jsc.exam.com.guidance.R;
10 | import jsc.exam.com.guidance.bean.ClassItem;
11 | import jsc.exam.com.guidance.utils.CompatResourceUtils;
12 | import jsc.exam.com.guidance.widgets.JSCItemLayout;
13 |
14 | public class ClassItemAdapter extends BaseRecyclerViewAdapter
Email:1006368252@qq.com
7 | *
QQ:1006368252
8 | *
https://github.com/JustinRoom/Guidance
9 | *
10 | * @author jiangshicheng
11 | */
12 | public interface ViewAdapter
Email:1006368252@qq.com
41 | *
QQ:1006368252
42 | *
https://github.com/JustinRoom/Guidance
43 | *
44 | * @author jiangshicheng
45 | */
46 | public class AboutFragment extends Fragment {
47 |
48 | @Nullable
49 | @Override
50 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
51 | View root = inflater.inflate(R.layout.fragment_abount, container, false);
52 | TextView tvVersion = root.findViewById(R.id.tv_version);
53 | TextView tvUpdateContent = root.findViewById(R.id.tv_update_content);
54 | TextView tvBuildTime = root.findViewById(R.id.tv_build_time);
55 | try {
56 | PackageInfo packageInfo = inflater.getContext().getPackageManager().getPackageInfo(inflater.getContext().getPackageName(), PackageManager.GET_GIDS);
57 | tvVersion.setText(String.format(Locale.CHINA, "version:%s", packageInfo.versionName));
58 | } catch (PackageManager.NameNotFoundException e) {
59 | e.printStackTrace();
60 | }
61 | tvBuildTime.setText(String.format(Locale.CHINA, "build time:%s", BuildConfig.BUILD_TIME));
62 |
63 | root.findViewById(R.id.btn_check_update).setOnClickListener(new View.OnClickListener() {
64 | @Override
65 | public void onClick(View v) {
66 | v.setEnabled(false);
67 | checkUpdate();
68 | }
69 | });
70 | tvUpdateContent.setText("当前版本更新内容:" + getString(R.string.app_update_content));
71 | return root;
72 | }
73 |
74 | private void checkUpdate() {
75 | OkHttpClient client = new CustomHttpClient()
76 | .addHeader(new Pair<>("token", ""))
77 | .setConnectTimeout(5_000)
78 | .setShowLog(true)
79 | .createOkHttpClient();
80 | Retrofit retrofit = new CustomRetrofit()
81 | //我在app的build.gradle文件的defaultConfig标签里定义了BASE_URL
82 | .setBaseUrl(BuildConfig.BASE_URL)
83 | .setOkHttpClient(client)
84 | .createRetrofit();
85 | Disposable disposable = retrofit.create(ApiService.class)
86 | .getVersionInfo()
87 | .subscribeOn(Schedulers.io())
88 | .observeOn(AndroidSchedulers.mainThread())
89 | .subscribe(new Consumer
Email:1006368252@qq.com
26 | *
QQ:1006368252
27 | *
https://github.com/JustinRoom/GuidanceDemo
28 | *
29 | * @author jiangshicheng
30 | */
31 | public class GuidanceDialogFragment extends BaseFragment {
32 |
33 | View btnShowAgain;
34 |
35 | @Nullable
36 | @Override
37 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
38 | View root = inflater.inflate(R.layout.fragment_guidance_dialog, container, false);
39 | btnShowAgain = root.findViewById(R.id.btn_show_again);
40 | btnShowAgain.setOnClickListener(new View.OnClickListener() {
41 | @Override
42 | public void onClick(View v) {
43 | showGuidanceDialog();
44 | }
45 | });
46 | return root;
47 | }
48 |
49 | @Override
50 | void onLoadData(Context context) {
51 | btnShowAgain.postDelayed(new Runnable() {
52 | @Override
53 | public void run() {
54 | showGuidanceDialog();
55 | }
56 | }, 100);
57 | }
58 |
59 | private void showGuidanceDialog() {
60 | final GuidanceDialog dialog = new GuidanceDialog(getContext());
61 | dialog.setTargetClickListener(new OnTargetClickListener() {
62 | @Override
63 | public boolean onTargetClick(GuidanceLayout layout) {
64 | Toast.makeText(layout.getContext(), "clicked me", Toast.LENGTH_SHORT).show();
65 | switch (layout.getCurStepIndex()) {
66 | case 0:
67 | showStep(layout, R.id.item_layout_1);
68 | return true;
69 | case 1:
70 | showStep(layout, R.id.item_layout_2);
71 | return true;
72 | case 2:
73 | showStep(layout, R.id.item_layout_3);
74 | return true;
75 | default:
76 | return false;
77 | }
78 | }
79 | });
80 | dialog.show();
81 | GuidanceLayout guidanceLayout = dialog.getGuidanceLayout();
82 | if (guidanceLayout == null)
83 | return;
84 | showStep(guidanceLayout, R.id.item_layout_0);
85 | }
86 |
87 | private void showStep(GuidanceLayout layout, int targetViewId) {
88 | layout.removeAllCustomViews();
89 | showStep(layout, getView().findViewById(targetViewId));
90 | }
91 |
92 | private void showStep(GuidanceLayout guidanceLayout, View target) {
93 | Context context = guidanceLayout.getContext();
94 | int statusBarHeight = ViewDrawingCacheUtils.getStatusBarHeight(context);
95 | int actionBarHeight = ViewDrawingCacheUtils.getActionBarSize(context);
96 | int[] location = ViewDrawingCacheUtils.getWindowLocation(target);
97 | guidanceLayout.updateTargetViewLocation(
98 | target, location[0],
99 | location[1] - statusBarHeight,
100 | new GuidanceLayout.OnInitRippleViewSizeListener() {
101 | @Override
102 | public int onInitializeRippleViewSize(@NonNull Bitmap bitmap) {
103 | return bitmap.getHeight();
104 | }
105 | },
106 | true,
107 | new GuidanceLayout.OnRippleViewLocationUpdatedCallback() {
108 | @Override
109 | public void onRippleViewLocationUpdated(@NonNull GuidanceRippleView rippleView, @NonNull Rect targetRect) {
110 |
111 | }
112 | });
113 |
114 | ImageView imageView = new ImageView(guidanceLayout.getContext());
115 | imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
116 | imageView.setImageResource(R.drawable.hand_o_up);
117 | guidanceLayout.addCustomView(imageView, new GuidanceLayout.OnCustomViewAddListener
Email:1006368252@qq.com
25 | *
QQ:1006368252
26 | *
https://github.com/JustinRoom/GuidanceDemo
27 | *
28 | * @author jiangshicheng
29 | */
30 | public class GuidanceLayoutFragment extends BaseFragment {
31 |
32 | Button btnShowAgain;
33 | GuidanceLayout layout;
34 |
35 | @Nullable
36 | @Override
37 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
38 | View root = inflater.inflate(R.layout.fragment_guidance_layout, container, false);
39 | btnShowAgain = root.findViewById(R.id.btn_show_again);
40 | layout = root.findViewById(R.id.guidance_layout);
41 | layout.setTargetClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View v) {
44 | Toast.makeText(layout.getContext(), "clicked me", Toast.LENGTH_SHORT).show();
45 | switch (layout.getCurStepIndex()) {
46 | case 0:
47 | showStep(layout, R.id.item_layout_1);
48 | break;
49 | case 1:
50 | showStep(layout, R.id.item_layout_2);
51 | break;
52 | case 2:
53 | showStep(layout, R.id.item_layout_3);
54 | break;
55 | default:
56 | layout.setVisibility(View.GONE);
57 | break;
58 | }
59 | }
60 | });
61 | btnShowAgain.setOnClickListener(new View.OnClickListener() {
62 | @Override
63 | public void onClick(View v) {
64 | layout.setVisibility(View.VISIBLE);
65 | layout.resetStepIndex();
66 | showStep(layout, R.id.item_layout_0);
67 | }
68 | });
69 | return root;
70 | }
71 |
72 | @Override
73 | void onLoadData(Context context) {
74 | layout.postDelayed(new Runnable() {
75 | @Override
76 | public void run() {
77 | showStep(layout, R.id.item_layout_0);
78 | }
79 | }, 100);
80 | }
81 |
82 | private void showStep(GuidanceLayout layout, int targetViewId) {
83 | layout.removeAllCustomViews();
84 | showStep(layout, getView().findViewById(targetViewId));
85 | }
86 |
87 | private void showStep(GuidanceLayout guidanceLayout, View target) {
88 | Context context = guidanceLayout.getContext();
89 | int statusBarHeight = ViewDrawingCacheUtils.getStatusBarHeight(context);
90 | int actionBarHeight = ViewDrawingCacheUtils.getActionBarSize(context);
91 | int[] location = ViewDrawingCacheUtils.getWindowLocation(target);
92 | guidanceLayout.updateTargetViewLocation(
93 | target, location[0],
94 | location[1] - statusBarHeight - actionBarHeight,
95 | new GuidanceLayout.OnInitRippleViewSizeListener() {
96 | @Override
97 | public int onInitializeRippleViewSize(@NonNull Bitmap bitmap) {
98 | return bitmap.getHeight();
99 | }
100 | },
101 | true,
102 | new GuidanceLayout.OnRippleViewLocationUpdatedCallback() {
103 | @Override
104 | public void onRippleViewLocationUpdated(@NonNull GuidanceRippleView rippleView, @NonNull Rect targetRect) {
105 |
106 | }
107 | });
108 |
109 | ImageView imageView = new ImageView(guidanceLayout.getContext());
110 | imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
111 | imageView.setImageResource(R.drawable.hand_o_up);
112 | guidanceLayout.addCustomView(imageView, new GuidanceLayout.OnCustomViewAddListener
Email:1006368252@qq.com
27 | *
QQ:1006368252
28 | *
https://github.com/JustinRoom/GuidanceDemo
29 | *
30 | * @author jiangshicheng
31 | */
32 | public class GuidancePopupWindowFragment extends BaseFragment {
33 |
34 | Button btnContent;
35 |
36 | @Nullable
37 | @Override
38 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
39 | View root = inflater.inflate(R.layout.fragment_guidance_popup_window, container, false);
40 | btnContent = root.findViewById(R.id.btn_content);
41 | root.findViewById(R.id.btn_content).setOnClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View v) {
44 | showContentGuidance();
45 | }
46 | });
47 | root.findViewById(R.id.btn_window).setOnClickListener(new View.OnClickListener() {
48 | @Override
49 | public void onClick(View v) {
50 | // showWindowGuidanceDialog();
51 | }
52 | });
53 | return root;
54 | }
55 |
56 | @Override
57 | void onLoadData(Context context) {
58 | btnContent.postDelayed(new Runnable() {
59 | @Override
60 | public void run() {
61 | showContentGuidance();
62 | }
63 | }, 100);
64 | }
65 |
66 | private void showContentGuidance() {
67 | final GuidancePopupWindow popupWindow = new GuidancePopupWindow(getActivity());
68 | popupWindow.setTargetClickListener(new OnTargetClickListener() {
69 | @Override
70 | public boolean onTargetClick(GuidanceLayout layout) {
71 | Toast.makeText(layout.getContext(), "clicked me", Toast.LENGTH_SHORT).show();
72 | switch (layout.getCurStepIndex()) {
73 | case 0:
74 | layout.removeAllCustomViews();
75 | showStep(layout, R.id.item_layout_1);
76 | return true;
77 | case 1:
78 | layout.removeAllCustomViews();
79 | showStep(layout, R.id.item_layout_2);
80 | return true;
81 | case 2:
82 | layout.removeAllCustomViews();
83 | showStep(layout, R.id.item_layout_3);
84 | return true;
85 | default:
86 | return false;
87 | }
88 | }
89 | });
90 | popupWindow.show();
91 | GuidanceLayout guidanceLayout = popupWindow.getGuidanceLayout();
92 | showStep(guidanceLayout, R.id.item_layout_0);
93 |
94 | }
95 |
96 | private void showStep(GuidanceLayout layout, int targetViewId) {
97 | layout.removeAllCustomViews();
98 | showStep(layout, getView().findViewById(targetViewId));
99 | }
100 |
101 | private void showStep(GuidanceLayout guidanceLayout, View target) {
102 | int statusBarHeight = ViewDrawingCacheUtils.getStatusBarHeight(getContext());
103 | int actionBarHeight = ViewDrawingCacheUtils.getActionBarSize(getContext());
104 | int[] location = ViewDrawingCacheUtils.getWindowLocation(target);
105 | guidanceLayout.updateTargetViewLocation(
106 | target, location[0],
107 | location[1] - statusBarHeight - actionBarHeight,
108 | new GuidanceLayout.OnInitRippleViewSizeListener() {
109 | @Override
110 | public int onInitializeRippleViewSize(@NonNull Bitmap bitmap) {
111 | return bitmap.getHeight();
112 | }
113 | }, true,
114 | new GuidanceLayout.OnRippleViewLocationUpdatedCallback() {
115 | @Override
116 | public void onRippleViewLocationUpdated(@NonNull GuidanceRippleView rippleView, @NonNull Rect targetRect) {
117 |
118 | }
119 | });
120 |
121 | ImageView imageView = new ImageView(guidanceLayout.getContext());
122 | imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
123 | imageView.setImageResource(R.drawable.hand_o_up);
124 | guidanceLayout.addCustomView(imageView, new GuidanceLayout.OnCustomViewAddListener
Email:1006368252@qq.com
16 | *
QQ:1006368252
17 | *
https://github.com/JustinRoom/GuidanceDemo
18 | *
19 | * @author jiangshicheng
20 | */
21 | public class GuidanceRippleViewFragment extends BaseFragment {
22 |
23 | GuidanceRippleView rippleView1;
24 | GuidanceRippleView rippleView2;
25 | GuidanceRippleView rippleView3;
26 | GuidanceRippleView rippleView4;
27 |
28 | @Nullable
29 | @Override
30 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
31 | View root = inflater.inflate(R.layout.fragment_guidance_ripple_view, container, false);
32 | rippleView1 = root.findViewById(R.id.ripple_view_1);
33 | rippleView2 = root.findViewById(R.id.ripple_view_2);
34 | rippleView3 = root.findViewById(R.id.ripple_view_3);
35 | rippleView4 = root.findViewById(R.id.ripple_view_4);
36 |
37 | root.findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
38 | @Override
39 | public void onClick(View v) {
40 | rippleView1.start();
41 | rippleView2.start();
42 | rippleView3.start();
43 | rippleView4.start();
44 | getView().findViewById(R.id.btn_start).setEnabled(false);
45 | getView().findViewById(R.id.btn_pause).setEnabled(true);
46 | getView().findViewById(R.id.btn_resume).setEnabled(false);
47 | getView().findViewById(R.id.btn_stop).setEnabled(true);
48 | }
49 | });
50 | root.findViewById(R.id.btn_pause).setOnClickListener(new View.OnClickListener() {
51 | @Override
52 | public void onClick(View v) {
53 | rippleView1.pause();
54 | rippleView2.pause();
55 | rippleView3.pause();
56 | rippleView4.pause();
57 | getView().findViewById(R.id.btn_start).setEnabled(false);
58 | getView().findViewById(R.id.btn_pause).setEnabled(false);
59 | getView().findViewById(R.id.btn_resume).setEnabled(true);
60 | getView().findViewById(R.id.btn_stop).setEnabled(true);
61 | }
62 | });
63 | root.findViewById(R.id.btn_resume).setOnClickListener(new View.OnClickListener() {
64 | @Override
65 | public void onClick(View v) {
66 | rippleView1.resume();
67 | rippleView2.resume();
68 | rippleView3.resume();
69 | rippleView4.resume();
70 | getView().findViewById(R.id.btn_start).setEnabled(false);
71 | getView().findViewById(R.id.btn_pause).setEnabled(true);
72 | getView().findViewById(R.id.btn_resume).setEnabled(false);
73 | getView().findViewById(R.id.btn_stop).setEnabled(true);
74 | }
75 | });
76 | root.findViewById(R.id.btn_stop).setOnClickListener(new View.OnClickListener() {
77 | @Override
78 | public void onClick(View v) {
79 | rippleView1.stop();
80 | rippleView2.stop();
81 | rippleView3.stop();
82 | rippleView4.stop();
83 | getView().findViewById(R.id.btn_start).setEnabled(true);
84 | getView().findViewById(R.id.btn_pause).setEnabled(false);
85 | getView().findViewById(R.id.btn_resume).setEnabled(false);
86 | getView().findViewById(R.id.btn_stop).setEnabled(false);
87 | }
88 | });
89 | return root;
90 | }
91 |
92 | @Override
93 | void onLoadData(Context context) {
94 |
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/src/main/java/jsc/exam/com/guidance/retrofit/ApiService.java:
--------------------------------------------------------------------------------
1 | package jsc.exam.com.guidance.retrofit;
2 |
3 | import io.reactivex.Observable;
4 | import jsc.exam.com.guidance.BuildConfig;
5 | import retrofit2.http.GET;
6 |
7 | public interface ApiService {
8 |
9 | @GET(BuildConfig.VERSION_URL)
10 | Observable
Email:1006368252@qq.com
26 | *
QQ:1006368252
27 | *
https://github.com/JustinRoom/Guidance
28 | *
29 | * create time: 6/6/2018 6:02 PM
30 | * @author jiangshicheng
31 | */
32 | public class CustomHttpClient {
33 | private boolean showLog = false;
34 | private int connectTimeout = 10_000;
35 | private int readTimeout = 10_000;
36 | private int writeTimeout = 10_000;
37 | private List
Email:1006368252@qq.com
17 | *
QQ:1006368252
18 | *
https://github.com/JustinRoom/Guidance
19 | *
20 | * @author jiangshicheng
21 | */
22 | public class CustomRetrofit {
23 | private String baseUrl;
24 | private OkHttpClient okHttpClient;
25 | private List
Email:1006368252@qq.com
15 | *
QQ:1006368252
16 | *
https://github.com/JustinRoom/Guidance
17 | *
18 | * create time: 6/7/2018 1:00 PM
19 | * @author jiangshicheng
20 | */
21 | public abstract class LoadingDialogObserver
Email:1006368252@qq.com
15 | *
QQ:1006368252
16 | *
https://github.com/JustinRoom/Guidance
17 | *
18 | * @author jiangshicheng
19 | */
20 | public class CompatResourceUtils {
21 |
22 | //color
23 | public static int getColor(@NonNull Context context, @ColorRes int resId){
24 | int color;
25 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
26 | color = context.getResources().getColor(resId, context.getTheme());
27 | } else {
28 | color = context.getResources().getColor(resId);
29 | }
30 | return color;
31 | }
32 |
33 | //drawable-xxhdpi
34 | public static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId){
35 | Drawable drawable;
36 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M){
37 | drawable = context.getResources().getDrawable(resId, context.getTheme());
38 | } else {
39 | drawable = context.getResources().getDrawable(resId);
40 | }
41 | return drawable;
42 | }
43 |
44 | //dimension
45 | public static int getDimensionPixelSize(@NonNull Context context, @DimenRes int id){
46 | return context.getResources().getDimensionPixelSize(id);
47 | }
48 |
49 | public static int getDimensionPixelSize(@NonNull View view, @DimenRes int id){
50 | return view.getResources().getDimensionPixelSize(id);
51 | }
52 |
53 | public static int getDimensionPixelSize(@NonNull Fragment fragment, @DimenRes int id){
54 | return fragment.getResources().getDimensionPixelSize(id);
55 | }
56 |
57 | public static int dp2Px(@NonNull Context context, float dp){
58 | return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()) + .5f);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/jsc/exam/com/guidance/utils/ConnectivityHelper.java:
--------------------------------------------------------------------------------
1 | package jsc.exam.com.guidance.utils;
2 |
3 | import android.content.Context;
4 | import android.net.ConnectivityManager;
5 | import android.net.NetworkInfo;
6 |
7 | import java.util.Objects;
8 |
9 | /**
10 | *
11 | * ConnectivityHelper 网络工具
12 | *
13 | *
14 | */
15 | public class ConnectivityHelper {
16 |
17 | public static boolean isNetworkAvailable(Context context) {
18 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
19 | Objects.requireNonNull(cm);
20 | NetworkInfo networkInfo = cm.getActiveNetworkInfo();
21 | return networkInfo != null && networkInfo.isAvailable();
22 | }
23 |
24 | /**
25 | * 判断网络是否可用
26 | *
27 | * @param context context
28 | * @return boolean
29 | */
30 | public static boolean isConnectivityAvailable(Context context) {
31 | // 判断网络是否可用
32 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
33 | Objects.requireNonNull(cm);
34 | NetworkInfo info = cm.getActiveNetworkInfo();
35 | if (info == null || !info.isConnected()) {
36 | return false;
37 | }
38 | return info.isAvailable() || info.isRoaming();
39 | }
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/jsc/exam/com/guidance/utils/WindowUtils.java:
--------------------------------------------------------------------------------
1 | package jsc.exam.com.guidance.utils;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.drawable.Drawable;
7 | import android.os.Build;
8 | import android.support.annotation.NonNull;
9 | import android.util.TypedValue;
10 |
11 | /**
12 | *
Email:1006368252@qq.com
13 | *
QQ:1006368252
14 | *
https://github.com/JustinRoom/Guidance
15 | *
16 | * @author jiangshicheng
17 | */
18 | public final class WindowUtils {
19 |
20 | /**
21 | * Get status bar height.
22 | *
23 | * @param context context
24 | * @return the height of status bar
25 | */
26 | public static int getStatusBarHeight(@NonNull Context context) {
27 | int statusBarHeight = 0;
28 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
29 | if (resourceId > 0) {
30 | statusBarHeight = CompatResourceUtils.getDimensionPixelSize(context, resourceId);
31 | }
32 | return statusBarHeight;
33 | }
34 |
35 | /**
36 | * Get action bar height.
37 | *
38 | * @param context context
39 | * @return the height of action bar
40 | */
41 | public static int getActionBarSize(@NonNull Context context) {
42 | TypedValue typedValue = new TypedValue();
43 | int[] attribute = new int[]{android.R.attr.actionBarSize};
44 | TypedArray array = context.obtainStyledAttributes(typedValue.resourceId, attribute);
45 | int actionBarSize = array.getDimensionPixelSize(0, 0);
46 | array.recycle();
47 | return actionBarSize;
48 | }
49 |
50 | /**
51 | * Get system selectable item background borderless.
52 | * @param context context
53 | * @return selectable item background borderless
54 | */
55 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
56 | public static Drawable getSelectableItemBackgroundBorderless(Context context){
57 | TypedValue typedValue = new TypedValue();
58 | context.getTheme().resolveAttribute(android.R.attr.selectableItemBackgroundBorderless, typedValue, true);
59 | int[] attribute = new int[]{android.R.attr.selectableItemBackgroundBorderless};
60 | TypedArray typedArray = context.obtainStyledAttributes(typedValue.resourceId, attribute);
61 | Drawable drawable = typedArray.getDrawable(0);
62 | typedArray.recycle();
63 | return drawable;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/jsc/exam/com/guidance/widgets/DotView.java:
--------------------------------------------------------------------------------
1 | package jsc.exam.com.guidance.widgets;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.graphics.Outline;
6 | import android.graphics.Path;
7 | import android.os.Build;
8 | import android.support.annotation.IntDef;
9 | import android.support.annotation.IntRange;
10 | import android.support.annotation.Nullable;
11 | import android.support.v7.widget.AppCompatTextView;
12 | import android.util.AttributeSet;
13 | import android.util.Log;
14 | import android.view.Gravity;
15 | import android.view.View;
16 | import android.view.ViewOutlineProvider;
17 |
18 | import java.lang.annotation.Retention;
19 | import java.lang.annotation.RetentionPolicy;
20 |
21 | /**
22 | *
Email:1006368252@qq.com
23 | *
QQ:1006368252
24 | *
https://github.com/JustinRoom/Guidance
25 | *
26 | * @author jiangshicheng
27 | */
28 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
29 | public class DotView extends AppCompatTextView {
30 |
31 | public final static int CIRCULAR = 0;
32 | public final static int SQUARE = 1;
33 | public final static int ROUND_CORNER_SQUARE = 2;
34 | public final static int TRIANGLE = 3;
35 | @IntDef({CIRCULAR, SQUARE, ROUND_CORNER_SQUARE, TRIANGLE})
36 | @Retention(RetentionPolicy.SOURCE)
37 | public @interface DotShape {
38 | }
39 |
40 | int shape = CIRCULAR;
41 | float radius = 0;
42 | int unReadCount = 0;
43 |
44 | public DotView(Context context) {
45 | super(context);
46 | initAttr(context, null, 0);
47 | }
48 |
49 | public DotView(Context context, @Nullable AttributeSet attrs) {
50 | super(context, attrs);
51 | initAttr(context, attrs, 0);
52 | }
53 |
54 | public DotView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
55 | super(context, attrs, defStyleAttr);
56 | initAttr(context, attrs, defStyleAttr);
57 | }
58 |
59 | private void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
60 | setGravity(Gravity.CENTER);
61 | setClipToOutline(true);
62 | setOutlineProvider(new ViewOutlineProvider() {
63 | @Override
64 | public void getOutline(View view, Outline outline) {
65 | switch (shape){
66 | case CIRCULAR:
67 | outline.setOval(0, 0, view.getWidth(), view.getHeight());
68 | break;
69 | case SQUARE:
70 | outline.setRect(0, 0, view.getWidth(), view.getHeight());
71 | break;
72 | case ROUND_CORNER_SQUARE:
73 | outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), radius);
74 | break;
75 | case TRIANGLE:
76 | Path path = new Path();
77 | path.moveTo(view.getWidth() / 2.0f, 0);
78 | path.lineTo(0, view.getHeight());
79 | path.lineTo(view.getWidth(), view.getHeight());
80 | path.close();
81 | Log.i("DotView", "getOutline: isConvex =" + path.isConvex());
82 | outline.setConvexPath(path);
83 | break;
84 | }
85 | }
86 | });
87 | }
88 |
89 | public int getUnReadCount() {
90 | return unReadCount;
91 | }
92 |
93 | public void setUnReadCount(@IntRange(from = 0) int unReadCount) {
94 | this.unReadCount = unReadCount;
95 | if (unReadCount > 99)
96 | setText("99+");
97 | else if (unReadCount > 0)
98 | setText(String.valueOf(unReadCount));
99 | else
100 | setText("");
101 | }
102 |
103 | public void setShape(@DotShape int shape) {
104 | setShape(shape, 0);
105 | }
106 |
107 | public void setShape(@DotShape int dotShape, float roundRadius) {
108 | this.shape = dotShape;
109 | this.radius = roundRadius;
110 | invalidateOutline();
111 | }
112 |
113 | @Override
114 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
115 | super.onMeasure(widthMeasureSpec, widthMeasureSpec);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/app/src/main/java/jsc/exam/com/guidance/widgets/JSCItemLayout.java:
--------------------------------------------------------------------------------
1 | package jsc.exam.com.guidance.widgets;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Color;
6 | import android.support.annotation.DrawableRes;
7 | import android.support.annotation.Nullable;
8 | import android.support.annotation.StringRes;
9 | import android.support.v7.widget.AppCompatImageView;
10 | import android.util.AttributeSet;
11 | import android.util.TypedValue;
12 | import android.view.Gravity;
13 | import android.view.ViewGroup;
14 | import android.widget.FrameLayout;
15 | import android.widget.ImageView;
16 | import android.widget.LinearLayout;
17 | import android.widget.TextView;
18 |
19 | import jsc.exam.com.guidance.R;
20 | import jsc.exam.com.guidance.utils.CompatResourceUtils;
21 |
22 | /**
23 | *
Email:1006368252@qq.com
24 | *
QQ:1006368252
25 | *
https://github.com/JustinRoom/Guidance
26 | *
27 | * @author jiangshicheng
28 | */
29 | public class JSCItemLayout extends FrameLayout {
30 |
31 | private ImageView iconView;
32 | private TextView labelView;
33 | private TextView valueView;
34 | private DotView dotView;
35 | private ImageView arrowView;
36 |
37 | public JSCItemLayout(Context context) {
38 | super(context);
39 | initAttr(context, null, 0);
40 | }
41 |
42 | public JSCItemLayout(Context context, @Nullable AttributeSet attrs) {
43 | super(context, attrs);
44 | initAttr(context, attrs, 0);
45 | }
46 |
47 | public JSCItemLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
48 | super(context, attrs, defStyleAttr);
49 | initAttr(context, attrs, defStyleAttr);
50 | }
51 |
52 | private void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
53 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.JSCItemLayout, defStyleAttr, 0);
54 |
55 | LayoutParams contentParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
56 | contentParams.gravity = Gravity.CENTER_VERTICAL;
57 | LinearLayout layout = new LinearLayout(context);
58 | layout.setOrientation(LinearLayout.HORIZONTAL);
59 | layout.setGravity(Gravity.CENTER_VERTICAL);
60 | addView(layout, contentParams);
61 |
62 | //icon
63 | iconView = new AppCompatImageView(context);
64 | iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
65 | layout.addView(iconView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
66 | iconView.setImageResource(a.getResourceId(R.styleable.JSCItemLayout_il_icon, R.drawable.kit_ic_assignment_blue_24dp));
67 |
68 | //label
69 | labelView = new TextView(context);
70 | labelView.setTextColor(0xFF666666);
71 | labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
72 | layout.addView(labelView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
73 | if (a.hasValue(R.styleable.JSCItemLayout_il_label)) {
74 | labelView.setText(a.getString(R.styleable.JSCItemLayout_il_label));
75 | }
76 |
77 | //value
78 | valueView = new TextView(context);
79 | valueView.setTextColor(0xFF333333);
80 | valueView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
81 | layout.addView(valueView, new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1));
82 | if (a.hasValue(R.styleable.JSCItemLayout_il_value)) {
83 | valueView.setText(a.getString(R.styleable.JSCItemLayout_il_value));
84 | }
85 |
86 | //red dot
87 | int size = CompatResourceUtils.getDimensionPixelSize(this, R.dimen.space_8);
88 | dotView = new DotView(context);
89 | dotView.setBackgroundColor(Color.RED);
90 | dotView.setTextColor(Color.WHITE);
91 | if (a.hasValue(R.styleable.JSCItemLayout_il_dotSize)){
92 | size = a.getDimensionPixelSize(R.styleable.JSCItemLayout_il_dotSize, 0);
93 | }
94 | dotView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
95 | layout.addView(dotView, new LinearLayout.LayoutParams(size, size));
96 | showDotView(a.getBoolean(R.styleable.JSCItemLayout_il_showDot, false));
97 |
98 | //right arrow
99 | arrowView = new AppCompatImageView(context);
100 | arrowView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
101 | layout.addView(arrowView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
102 | arrowView.setImageResource(a.getResourceId(R.styleable.JSCItemLayout_il_arrowIcon, R.drawable.kit_ic_chevron_right_gray_24dp));
103 |
104 | a.recycle();
105 | }
106 |
107 | public ImageView getIconView() {
108 | return iconView;
109 | }
110 |
111 | public TextView getLabelView() {
112 | return labelView;
113 | }
114 |
115 | public TextView getValueView() {
116 | return valueView;
117 | }
118 |
119 | public DotView getDotView() {
120 | return dotView;
121 | }
122 |
123 | public ImageView getArrowView() {
124 | return arrowView;
125 | }
126 |
127 | public void showIconView(boolean show) {
128 | iconView.setVisibility(show ? VISIBLE : GONE);
129 | }
130 |
131 | public void showDotView(boolean show) {
132 | dotView.setVisibility(show ? VISIBLE : GONE);
133 | }
134 |
135 | public void showArrowView(boolean show){
136 | arrowView.setVisibility(show ? VISIBLE : GONE);
137 | }
138 |
139 | public int getUnreadCount() {
140 | return dotView.getUnReadCount();
141 | }
142 |
143 | public void setUnreadCount(int unreadCount) {
144 | dotView.setUnReadCount(unreadCount);
145 | }
146 |
147 | public void setIcon(@DrawableRes int resId) {
148 | iconView.setImageResource(resId);
149 | }
150 |
151 | public void setLabel(CharSequence label) {
152 | labelView.setText(label);
153 | }
154 |
155 | public void setLabel(@StringRes int resId) {
156 | labelView.setText(resId);
157 | }
158 |
159 | public void setValue(CharSequence label) {
160 | valueView.setText(label);
161 | }
162 |
163 | public void setValue(@StringRes int resId) {
164 | valueView.setText(resId);
165 | }
166 |
167 | public void setArrow(@DrawableRes int resId) {
168 | arrowView.setImageResource(resId);
169 | }
170 | }
171 |
--------------------------------------------------------------------------------
/app/src/main/java/jsc/exam/com/guidance/widgets/SquareImageView.java:
--------------------------------------------------------------------------------
1 | package jsc.exam.com.guidance.widgets;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.AppCompatImageView;
5 | import android.util.AttributeSet;
6 |
7 | public class SquareImageView extends AppCompatImageView {
8 | public SquareImageView(Context context) {
9 | super(context);
10 | }
11 |
12 | public SquareImageView(Context context, AttributeSet attrs) {
13 | super(context, attrs);
14 | }
15 |
16 | public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) {
17 | super(context, attrs, defStyleAttr);
18 | }
19 |
20 | @Override
21 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
22 | super.onMeasure(widthMeasureSpec, widthMeasureSpec);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/jsc/exam/com/guidance/widgets/dialog/BottomShowDialog.java:
--------------------------------------------------------------------------------
1 | package jsc.exam.com.guidance.widgets.dialog;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Color;
7 | import android.os.Bundle;
8 | import android.support.annotation.NonNull;
9 | import android.util.TypedValue;
10 | import android.view.Gravity;
11 | import android.view.ViewGroup;
12 | import android.view.Window;
13 | import android.view.WindowManager;
14 | import android.widget.ImageView;
15 | import android.widget.LinearLayout;
16 | import android.widget.TextView;
17 |
18 | import jsc.exam.com.guidance.R;
19 |
20 | /**
21 | *
Email:1006368252@qq.com
22 | *
QQ:1006368252
23 | *
https://github.com/JustinRoom/Guidance
24 | *
25 | * @author jiangshicheng
26 | */
27 | public class BottomShowDialog extends Dialog {
28 |
29 | CharSequence title;
30 | Bitmap bitmap;
31 |
32 | public BottomShowDialog( @NonNull Context context) {
33 | this(context, R.style.Theme_AppCompat_Dialog);
34 | }
35 |
36 | public BottomShowDialog( @NonNull Context context, int themeResId) {
37 | super(context, themeResId);
38 | }
39 |
40 | @Override
41 | protected void onCreate(Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 | requestWindowFeature(Window.FEATURE_NO_TITLE);
44 | LinearLayout layout = new LinearLayout(getContext());
45 | layout.setOrientation(LinearLayout.VERTICAL);
46 | layout.setGravity(Gravity.CENTER_HORIZONTAL);
47 | //
48 | TextView textView = new TextView(getContext());
49 | textView.setBackgroundColor(getContext().getResources().getColor(R.color.colorAccent));
50 | textView.setGravity(Gravity.CENTER_HORIZONTAL);
51 | textView.setText(title);
52 | textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
53 | textView.setTextColor(0xFF333333);
54 | textView.setPadding(0, 16, 0, 16);
55 | layout.addView(textView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
56 | //
57 | LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
58 | params.topMargin = 24;
59 | params.bottomMargin = 24;
60 | ImageView imageView = new ImageView(getContext());
61 | imageView.setImageBitmap(bitmap);
62 | layout.addView(imageView, params);
63 | setContentView(layout);
64 |
65 | if (getWindow() != null) {
66 | getWindow().setGravity(Gravity.BOTTOM);
67 | getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
68 | getWindow().setBackgroundDrawable(null);
69 | getWindow().getDecorView().setBackgroundColor(Color.WHITE);
70 | }
71 | }
72 |
73 | @Override
74 | public void show() {
75 | super.show();
76 | }
77 |
78 | @Override
79 | public void setTitle(CharSequence title) {
80 | this.title = title;
81 | }
82 |
83 | public void setBitmap(Bitmap bitmap) {
84 | this.bitmap = bitmap;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v21/ripple_round_corner_white_r4.xml:
--------------------------------------------------------------------------------
1 |
2 |
Email:1006368252@qq.com
16 | *
QQ:1006368252
17 | *
https://github.com/JustinRoom/GuidanceDemo
18 | *
19 | * @author jiangshicheng
20 | */
21 | public class GuidanceDialog extends AppCompatDialog {
22 |
23 | private GuidanceLayout guidanceLayout;
24 | private int width = ViewGroup.LayoutParams.MATCH_PARENT;
25 | private int height = ViewGroup.LayoutParams.MATCH_PARENT;
26 | private OnTargetClickListener listener = null;
27 |
28 | public GuidanceDialog(@NonNull Context context) {
29 | super(context);
30 | setCancelable(false);
31 | setCanceledOnTouchOutside(false);
32 | }
33 |
34 | public GuidanceDialog(@NonNull Context context, int themeResId) {
35 | super(context, themeResId);
36 | setCancelable(false);
37 | setCanceledOnTouchOutside(false);
38 | }
39 |
40 | @Override
41 | protected void onCreate(Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 | supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
44 | guidanceLayout = new GuidanceLayout(getContext());
45 | guidanceLayout.setTargetClickListener(new View.OnClickListener() {
46 | @Override
47 | public void onClick(View v) {
48 | if (listener == null || !listener.onTargetClick(guidanceLayout))
49 | dismiss();
50 | }
51 | });
52 | setContentView(guidanceLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
53 | if (getWindow() != null) {
54 | //设置window背景,默认的背景会有Padding值,不能全屏。当然不一定要是透明,你可以设置其他背景,替换默认的背景即可。
55 | getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
56 | //一定要在setContentView之后调用,否则无效
57 | getWindow().setLayout(width, height);
58 | }
59 | }
60 |
61 | public void setTargetClickListener(OnTargetClickListener listener) {
62 | this.listener = listener;
63 | }
64 |
65 | public void setSize(int width, int height) {
66 | this.width = width;
67 | this.height = height;
68 | }
69 |
70 | @Nullable
71 | public GuidanceLayout getGuidanceLayout() {
72 | return guidanceLayout;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/guidanceLibrary/src/main/java/jsc/kit/guidance/GuidanceLayout.java:
--------------------------------------------------------------------------------
1 | package jsc.kit.guidance;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Rect;
6 | import android.support.annotation.LayoutRes;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.util.AttributeSet;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.FrameLayout;
14 | import android.widget.ImageView;
15 |
16 | /**
17 | *
Email:1006368252@qq.com
18 | *
QQ:1006368252
19 | *
https://github.com/JustinRoom/GuidanceDemo
20 | *
21 | * @author jiangshicheng
22 | */
23 | public class GuidanceLayout extends FrameLayout {
24 |
25 | private static final String TAG = "GuidanceLayout";
26 | private ImageView targetView;
27 | private GuidanceRippleView rippleViewView;
28 | private Rect targetRect = new Rect();
29 | private int curStepIndex = -1;
30 |
31 | public GuidanceLayout(@NonNull Context context) {
32 | super(context);
33 | initAttr(context, null, 0);
34 | }
35 |
36 | public GuidanceLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
37 | super(context, attrs);
38 | initAttr(context, attrs, 0);
39 | }
40 |
41 | public GuidanceLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
42 | super(context, attrs, defStyleAttr);
43 | initAttr(context, attrs, defStyleAttr);
44 | }
45 |
46 | public void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
47 | setEnabled(false);
48 | targetView = new ImageView(context);
49 | rippleViewView = new GuidanceRippleView(context);
50 | rippleViewView.initAttr(context, attrs, defStyleAttr);
51 | addView(targetView, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
52 | addView(rippleViewView, new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
53 | }
54 |
55 | @Override
56 | public boolean onTouchEvent(MotionEvent event) {
57 | return true;
58 | }
59 |
60 | @NonNull
61 | public Rect getTargetRect() {
62 | return targetRect;
63 | }
64 |
65 | public int getCurStepIndex() {
66 | return curStepIndex;
67 | }
68 |
69 | public void resetStepIndex() {
70 | curStepIndex = -1;
71 | }
72 |
73 | /**
74 | * Set the click listener for {@link #targetView}.
75 | *
76 | * @param l target click listener
77 | */
78 | public void setTargetClickListener(View.OnClickListener l) {
79 | targetView.setOnClickListener(l);
80 | }
81 |
82 | /**
83 | * Remove all children except {@link #targetView} and {@link #rippleViewView}.
84 | */
85 | public void removeAllCustomViews() {
86 | int count = getChildCount();
87 | if (count > 2) {
88 | View[] children = new View[count - 2];
89 | int index = -1;
90 | for (int i = 0; i < getChildCount(); i++) {
91 | View child = getChildAt(i);
92 | if (child == targetView
93 | || child == rippleViewView)
94 | continue;
95 | index++;
96 | children[index] = child;
97 | }
98 | for (View child : children) {
99 | removeView(child);
100 | }
101 | }
102 | }
103 |
104 | /**
105 | * Add custom view into layout.
106 | *
107 | * @param customView custom view
108 | * @param l1 listener for creating layout params
109 | * @param l2 click listener
110 | */
111 | public
Email:1006368252@qq.com
18 | *
QQ:1006368252
19 | *
https://github.com/JustinRoom/GuidanceDemo
20 | *
21 | * @author jiangshicheng
22 | */
23 | public class GuidancePopupWindow {
24 |
25 | public static final int SHOW_IN_CONTENT = 0x10;
26 | public static final int SHOW_IN_WINDOW = 0x11;
27 |
28 | @IntDef({SHOW_IN_CONTENT, SHOW_IN_WINDOW})
29 | @Retention(RetentionPolicy.SOURCE)
30 | public @interface ShowType {
31 | }
32 |
33 | private Activity activity;
34 | private GuidanceLayout guidanceLayout;
35 | private int curShowType = -1;
36 | private OnTargetClickListener listener = null;
37 |
38 | public GuidancePopupWindow(@NonNull Activity activity) {
39 | this(activity, 0x99000000);
40 | }
41 |
42 | public GuidancePopupWindow(@NonNull Activity activity, @ColorInt int backgroundColor) {
43 | this.activity = activity;
44 | guidanceLayout = new GuidanceLayout(activity);
45 | guidanceLayout.setId(R.id.guidance_default_layout_id);
46 | guidanceLayout.setBackgroundColor(backgroundColor);
47 | guidanceLayout.setTargetClickListener(new View.OnClickListener() {
48 | @Override
49 | public void onClick(View v) {
50 | if (listener == null || !listener.onTargetClick(guidanceLayout))
51 | dismiss();
52 | }
53 | });
54 | }
55 |
56 | public void show() {
57 | show(SHOW_IN_CONTENT);
58 | }
59 |
60 | public void show(@ShowType int showType) {
61 | this.curShowType = showType;
62 | switch (curShowType) {
63 | case SHOW_IN_CONTENT:
64 | FrameLayout contentLayout = activity.findViewById(android.R.id.content);
65 | if (!isGuidanceLayoutAdded(guidanceLayout)) {
66 | contentLayout.addView(guidanceLayout);
67 | }
68 | break;
69 | case SHOW_IN_WINDOW:
70 | WindowManager.LayoutParams params = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
71 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
72 | params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
73 | } else {
74 | params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
75 | }
76 | activity.getWindow().getWindowManager().addView(guidanceLayout, params);
77 | break;
78 | }
79 | }
80 |
81 | public void dismiss() {
82 | switch (curShowType) {
83 | case SHOW_IN_CONTENT:
84 | FrameLayout contentLayout = activity.findViewById(android.R.id.content);
85 | contentLayout.removeView(guidanceLayout);
86 | break;
87 | case SHOW_IN_WINDOW:
88 | activity.getWindow().getWindowManager().removeView(guidanceLayout);
89 | break;
90 | }
91 | }
92 |
93 | @NonNull
94 | public GuidanceLayout getGuidanceLayout() {
95 | return guidanceLayout;
96 | }
97 |
98 | public void setTargetClickListener(OnTargetClickListener listener) {
99 | this.listener = listener;
100 | }
101 |
102 | private boolean isGuidanceLayoutAdded(ViewGroup viewGroup) {
103 | for (int i = 0; i < viewGroup.getChildCount(); i++) {
104 | View child = viewGroup.getChildAt(i);
105 | if (child == guidanceLayout)
106 | return true;
107 | }
108 | return false;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/guidanceLibrary/src/main/java/jsc/kit/guidance/GuidanceRippleView.java:
--------------------------------------------------------------------------------
1 | package jsc.kit.guidance;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.support.annotation.Nullable;
9 | import android.util.AttributeSet;
10 | import android.view.View;
11 |
12 | /**
13 | *
Email:1006368252@qq.com
14 | *
QQ:1006368252
15 | *
https://github.com/JustinRoom/GuidanceDemo
16 | *
17 | * @author jiangshicheng
18 | */
19 | public class GuidanceRippleView extends View {
20 |
21 | private Paint paint;
22 | private int count;
23 | private int[] colors;
24 | private float space;
25 | private float speed;
26 | private boolean autoRun;
27 |
28 | private int frameCountPerSecond = 0;
29 | private float radius = 0;
30 | private boolean isRunning = false;
31 | private int clipWidth;
32 | private int clipHeight;
33 |
34 | public GuidanceRippleView(Context context) {
35 | super(context);
36 | initAttr(context, null, 0);
37 | }
38 |
39 | public GuidanceRippleView(Context context, @Nullable AttributeSet attrs) {
40 | super(context, attrs);
41 | initAttr(context, attrs, 0);
42 | }
43 |
44 | public GuidanceRippleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
45 | super(context, attrs, defStyleAttr);
46 | initAttr(context, attrs, defStyleAttr);
47 | }
48 |
49 | public void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
50 | paint = new Paint(Paint.ANTI_ALIAS_FLAG);
51 | paint.setStyle(Paint.Style.FILL);
52 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GuidanceRippleView, defStyleAttr, 0);
53 | count = a.getInteger(R.styleable.GuidanceRippleView_grvCount, 3);
54 | space = a.getDimension(R.styleable.GuidanceRippleView_grvSpace, 0);
55 | frameCountPerSecond = a.getInteger(R.styleable.GuidanceRippleView_grvSpeed, 48);
56 | autoRun = a.getBoolean(R.styleable.GuidanceRippleView_grvAutoRun, true);
57 | String colors = a.getString(R.styleable.GuidanceRippleView_grvColors);
58 | a.recycle();
59 | if (count < 1)
60 | throw new IllegalArgumentException("count must be more than one.");
61 | if (frameCountPerSecond < 1)
62 | throw new IllegalArgumentException("speed should be one frame per second at least.");
63 | this.colors = new int[count];
64 | if (colors != null) {
65 | colors = colors.trim();
66 | colors = colors.replace("{", "");
67 | colors = colors.replace("}", "");
68 | colors = colors.replace("(", "");
69 | colors = colors.replace(")", "");
70 | String[] colorSplit = colors.split(",");
71 | for (int i = 0; i < count; i++) {
72 | if (colorSplit.length > 0)
73 | this.colors[i] = Color.parseColor(colorSplit[i % colorSplit.length].trim());
74 | else
75 | this.colors[i] = Color.BLUE;
76 | }
77 | } else {
78 | for (int i = 0; i < count; i++) {
79 | this.colors[i] = Color.BLUE;
80 | }
81 | }
82 | }
83 |
84 | @Override
85 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
86 | super.onMeasure(widthMeasureSpec, widthMeasureSpec);
87 | if (space <= 0) {
88 | space = getMeasuredWidth() * 1.0f / (count * 2);
89 | }
90 | }
91 |
92 | @Override
93 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
94 | super.onSizeChanged(w, h, oldw, oldh);
95 | speed = -1;
96 | }
97 |
98 | @Override
99 | protected void onDraw(Canvas canvas) {
100 | if (speed <= 0) {
101 | speed = space / frameCountPerSecond;
102 | }
103 |
104 | if (clipWidth > 0 && clipHeight > 0) {
105 | int clipLeft = (getWidth() - clipWidth) / 2;
106 | int clipTop = (getHeight() - clipHeight) / 2;
107 | canvas.clipRect(clipLeft, clipTop, clipLeft + clipWidth, clipTop + clipHeight);
108 | }
109 |
110 | float maxRadius = getWidth() / 2.0f;
111 | int alpha = (int) (0xFF * (1 - radius / maxRadius) + .5f);
112 | for (int i = 0; i < count; i++) {
113 | paint.setColor(colors[i]);
114 | paint.setAlpha(alpha);
115 | float tempRadius = radius - space * i;
116 | if (tempRadius > 0)
117 | canvas.drawCircle(maxRadius, maxRadius, tempRadius, paint);
118 | }
119 | radius += speed;
120 | if (radius > maxRadius)
121 | radius = 0;
122 | if (isRunning)
123 | invalidate();
124 | }
125 |
126 | @Override
127 | protected void onAttachedToWindow() {
128 | super.onAttachedToWindow();
129 | if (autoRun)
130 | start();
131 | }
132 |
133 | @Override
134 | protected void onDetachedFromWindow() {
135 | stop();
136 | super.onDetachedFromWindow();
137 | }
138 |
139 | public void start() {
140 | if (isRunning)
141 | return;
142 | radius = 0;
143 | isRunning = true;
144 | invalidate();
145 | }
146 |
147 | public void stop() {
148 | radius = 0;
149 | isRunning = false;
150 | invalidate();
151 | }
152 |
153 | public void pause() {
154 | isRunning = false;
155 | }
156 |
157 | public void resume() {
158 | isRunning = true;
159 | invalidate();
160 | }
161 |
162 | /**
163 | * Whether run animation automatically when {@link #onAttachedToWindow()}.
164 | *
165 | * @param autoRun true, run animation automatically when {@link #onAttachedToWindow()}.
166 | */
167 | public void setAutoRun(boolean autoRun) {
168 | this.autoRun = autoRun;
169 | }
170 |
171 | /**
172 | * Set the ripple animation display area.
173 | *
174 | * @param clipWidth clip width
175 | * @param clipHeight clip height
176 | */
177 | public void setClip(int clipWidth, int clipHeight) {
178 | this.clipWidth = clipWidth;
179 | this.clipHeight = clipHeight;
180 | }
181 |
182 | /**
183 | * @param count ripple circle count
184 | * @param colors colors
185 | * @param space the radius offset of two ripple circle
186 | * @param frameCountPerSecond the speed of ripple animation
187 | */
188 | public void updateAnimation(int count, float space, int frameCountPerSecond, int... colors) {
189 | if (count < 1)
190 | throw new IllegalArgumentException("count must be more than one.");
191 | if (frameCountPerSecond < 1)
192 | throw new IllegalArgumentException("speed should be one frame per second at least.");
193 |
194 | this.colors = new int[count];
195 | if (colors == null || colors.length == 0)
196 | colors = new int[]{Color.BLUE};
197 | for (int i = 0; i < count; i++) {
198 | this.colors[i] = colors[i % colors.length];
199 | }
200 | this.count = count;
201 | this.space = space;
202 | this.speed = -1;
203 |
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/guidanceLibrary/src/main/java/jsc/kit/guidance/OnTargetClickListener.java:
--------------------------------------------------------------------------------
1 | package jsc.kit.guidance;
2 |
3 | public interface OnTargetClickListener {
4 | boolean onTargetClick(GuidanceLayout layout);
5 | }
--------------------------------------------------------------------------------
/guidanceLibrary/src/main/java/jsc/kit/guidance/PermissionUtils.java:
--------------------------------------------------------------------------------
1 | package jsc.kit.guidance;
2 |
3 | import android.content.Intent;
4 | import android.net.Uri;
5 | import android.os.Build;
6 | import android.provider.Settings;
7 | import android.support.annotation.NonNull;
8 | import android.support.v4.app.Fragment;
9 | import android.support.v4.app.FragmentActivity;
10 |
11 | /**
12 | *
Email:1006368252@qq.com
13 | *
QQ:1006368252
14 | *
https://github.com/JustinRoom/GuidanceDemo
15 | *
16 | * @author jiangshicheng
17 | */
18 | public class PermissionUtils {
19 |
20 | public static boolean checkOverlayPermission(@NonNull Fragment fragment, int requestCode){
21 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
22 | return true;
23 | if (!Settings.canDrawOverlays(fragment.getContext())) {
24 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
25 | intent.setData(Uri.parse("package:" + fragment.getContext().getPackageName()));
26 | fragment.startActivityForResult(intent, requestCode);
27 | }
28 | return true;
29 | }
30 |
31 | public static boolean checkOverlayPermission(@NonNull FragmentActivity activity, int requestCode){
32 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
33 | return true;
34 | if (!Settings.canDrawOverlays(activity)) {
35 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
36 | intent.setData(Uri.parse("package:" + activity.getPackageName()));
37 | activity.startActivityForResult(intent, requestCode);
38 | }
39 | return true;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/guidanceLibrary/src/main/java/jsc/kit/guidance/ViewDrawingCacheUtils.java:
--------------------------------------------------------------------------------
1 | package jsc.kit.guidance;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Canvas;
7 | import android.support.annotation.NonNull;
8 | import android.util.TypedValue;
9 | import android.view.View;
10 |
11 | /**
12 | *
Email:1006368252@qq.com
13 | *
QQ:1006368252
14 | *
https://github.com/JustinRoom/GuidanceDemo
15 | *
16 | * @author jiangshicheng
17 | */
18 | public class ViewDrawingCacheUtils {
19 |
20 | public static Bitmap getDrawingCache(@NonNull View view) {
21 | int width = view.getWidth();
22 | int height = view.getHeight();
23 | if (width + height == 0) {
24 | view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
25 | width = view.getMeasuredWidth();
26 | height = view.getMeasuredHeight();
27 | }
28 | Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
29 | Canvas canvas = new Canvas(bitmap);
30 | view.draw(canvas);
31 | return bitmap;
32 | }
33 |
34 | /**
35 | * Get status bar height.
36 | *
37 | * @param context context
38 | * @return the height of status bar
39 | */
40 | public static int getStatusBarHeight(@NonNull Context context) {
41 | int statusBarHeight = 0;
42 | int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
43 | if (resourceId > 0) {
44 | statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
45 | }
46 | return statusBarHeight;
47 | }
48 |
49 | /**
50 | * Get action bar height.
51 | *
52 | * @param context context
53 | * @return the height of action bar
54 | */
55 | public static int getActionBarSize(@NonNull Context context) {
56 | TypedValue typedValue = new TypedValue();
57 | int[] attribute = new int[]{android.R.attr.actionBarSize};
58 | TypedArray array = context.obtainStyledAttributes(typedValue.resourceId, attribute);
59 | int actionBarSize = array.getDimensionPixelSize(0, 0);
60 | array.recycle();
61 | return actionBarSize;
62 | }
63 |
64 | public static int[] getScreenLocation(@NonNull View view){
65 | int[] outLocation = new int[2];
66 | view.getLocationOnScreen(outLocation);
67 | return outLocation;
68 | }
69 |
70 | public static int[] getWindowLocation(@NonNull View view){
71 | int[] outLocation = new int[2];
72 | view.getLocationInWindow(outLocation);
73 | return outLocation;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/guidanceLibrary/src/main/res/values/guidance_attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |