guide初步
This commit is contained in:
parent
e0ede99c67
commit
994d205a7e
|
|
@ -88,6 +88,7 @@ dependencies {
|
||||||
implementation(libs.okhttp.logging)
|
implementation(libs.okhttp.logging)
|
||||||
implementation(libs.retrofit)
|
implementation(libs.retrofit)
|
||||||
implementation(libs.retrofit.kotlin.serialization)
|
implementation(libs.retrofit.kotlin.serialization)
|
||||||
|
//implementation 'com.github.hyy920109:GuidePro:1.0.3'
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
package com.gamedog.vididin.main
|
package com.gamedog.vididin.main
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import com.ama.core.architecture.util.eventbus.NotifyMan
|
|
||||||
import com.ama.core.architecture.util.setOnClickBatch
|
import com.ama.core.architecture.util.setOnClickBatch
|
||||||
import com.ama.core.architecture.widget.BindingDialog
|
import com.ama.core.architecture.widget.BindingDialog
|
||||||
import com.gamedog.vididin.VididinEvents
|
|
||||||
import com.vididin.real.money.game.databinding.DialogBeginnerGiftBinding
|
import com.vididin.real.money.game.databinding.DialogBeginnerGiftBinding
|
||||||
import com.gamedog.vididin.router.Router
|
|
||||||
|
|
||||||
|
|
||||||
class BeginnerGiftDialog(activity: Activity) : BindingDialog<DialogBeginnerGiftBinding>(activity, DialogBeginnerGiftBinding::inflate) {
|
class BeginnerGiftDialog(activity: Activity) : BindingDialog<DialogBeginnerGiftBinding>(activity, DialogBeginnerGiftBinding::inflate) {
|
||||||
|
|
@ -26,10 +23,10 @@ class BeginnerGiftDialog(activity: Activity) : BindingDialog<DialogBeginnerGiftB
|
||||||
setOnClickBatch(tvAction) {
|
setOnClickBatch(tvAction) {
|
||||||
when (this) {
|
when (this) {
|
||||||
tvAction -> {
|
tvAction -> {
|
||||||
/*if (mActivity is MainActivity) {
|
if (mActivity is MainActivity) {
|
||||||
(mActivity as MainActivity).switchTab(1)
|
(mActivity as MainActivity).switchTab(1)
|
||||||
}
|
}
|
||||||
NotifyMan.instance().sendEvent(VididinEvents.EVENT_JUMP_2_FIRST_WITHDRAW, null)*/
|
//NotifyMan.instance().sendEvent(VididinEvents.EVENT_JUMP_2_FIRST_WITHDRAW, null)*/
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -37,9 +34,7 @@ class BeginnerGiftDialog(activity: Activity) : BindingDialog<DialogBeginnerGiftB
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun gotoWatchVideo() {
|
|
||||||
Router.Withdraw.startActivity(mActivity)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import android.Manifest
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
|
import androidx.core.graphics.toColorInt
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
|
|
@ -14,6 +16,7 @@ import com.ama.core.architecture.appBase.AppViewsFragment
|
||||||
import com.ama.core.architecture.appBase.OnFragmentBackgroundListener
|
import com.ama.core.architecture.appBase.OnFragmentBackgroundListener
|
||||||
import com.ama.core.architecture.util.AndroidUtil
|
import com.ama.core.architecture.util.AndroidUtil
|
||||||
import com.ama.core.architecture.util.ResUtil
|
import com.ama.core.architecture.util.ResUtil
|
||||||
|
import com.ama.core.architecture.util.ResUtil.dp
|
||||||
import com.ama.core.architecture.util.eventbus.NotifyMan
|
import com.ama.core.architecture.util.eventbus.NotifyMan
|
||||||
import com.ama.core.architecture.util.permission.PermissionUtil
|
import com.ama.core.architecture.util.permission.PermissionUtil
|
||||||
import com.ama.core.architecture.util.setOnClickBatch
|
import com.ama.core.architecture.util.setOnClickBatch
|
||||||
|
|
@ -33,6 +36,11 @@ import com.gamedog.vididin.manager.TaskManager
|
||||||
import com.gamedog.vididin.manager.taskbeans.BaseTaskState.Companion.STATE_FINISH
|
import com.gamedog.vididin.manager.taskbeans.BaseTaskState.Companion.STATE_FINISH
|
||||||
import com.gamedog.vididin.manager.taskbeans.BaseTaskState.Companion.STATE_ONGOING
|
import com.gamedog.vididin.manager.taskbeans.BaseTaskState.Companion.STATE_ONGOING
|
||||||
import com.gamedog.vididin.router.Router
|
import com.gamedog.vididin.router.Router
|
||||||
|
import com.ama.core.architecture.highlightpro.HighlightPro
|
||||||
|
import com.ama.core.architecture.highlightpro.parameter.Constraints
|
||||||
|
import com.ama.core.architecture.highlightpro.parameter.HighlightParameter
|
||||||
|
import com.ama.core.architecture.highlightpro.parameter.MarginOffset
|
||||||
|
import com.ama.core.architecture.highlightpro.shape.RectShape
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.Runnable
|
import kotlinx.coroutines.Runnable
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
@ -136,6 +144,10 @@ class TasksFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnTab
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
mTaskConfig = TaskManager.instance().getTaskConfig()
|
mTaskConfig = TaskManager.instance().getTaskConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding?.llTaskGame?.postDelayed({
|
||||||
|
showGuide()
|
||||||
|
}, 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addDailySubTasks() {
|
private fun addDailySubTasks() {
|
||||||
|
|
@ -422,4 +434,39 @@ class TasksFragment : AppViewsFragment<ViewBinding, UiState, ViewModel>(), OnTab
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun showGuide() {
|
||||||
|
HighlightPro.with(this@TasksFragment)
|
||||||
|
.setHighlightParameter {
|
||||||
|
HighlightParameter.Builder()
|
||||||
|
.setHighlightView(binding!!.goldContainer)
|
||||||
|
.setTipsViewId(R.layout.guide_step_gold)
|
||||||
|
.setHighlightShape(RectShape(10.dp, 10.dp, 10.dp))
|
||||||
|
.setHighlightHorizontalPadding(0.dp)
|
||||||
|
.setConstraints(Constraints.TopToBottomOfHighlight + Constraints.EndToEndOfHighlight)
|
||||||
|
.setMarginOffset(MarginOffset(top = -20.dp.toInt()))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
.setHighlightParameter {
|
||||||
|
HighlightParameter.Builder()
|
||||||
|
.setHighlightView(binding!!.cashContainer)
|
||||||
|
.setTipsViewId(R.layout.guide_step_cash)
|
||||||
|
.setHighlightShape(RectShape(10.dp, 10.dp, 10.dp))
|
||||||
|
.setHighlightHorizontalPadding(0.dp)
|
||||||
|
.setConstraints(Constraints.TopToBottomOfHighlight + Constraints.EndToEndOfHighlight)
|
||||||
|
.setMarginOffset(MarginOffset(top = -20.dp.toInt()))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
.setBackgroundColor("#cc000000".toColorInt())
|
||||||
|
.setOnShowCallback { index ->
|
||||||
|
|
||||||
|
}
|
||||||
|
.setOnDismissCallback {
|
||||||
|
|
||||||
|
}
|
||||||
|
.interceptBackPressed(true)
|
||||||
|
.show()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/tv_tips"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="right"
|
||||||
|
android:layout_marginLeft="100dp"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:src="@mipmap/guide_finger_b" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="15dp"
|
||||||
|
android:layout_marginTop="70dp"
|
||||||
|
android:padding="15dp"
|
||||||
|
android:background="@mipmap/guide_bg_right">
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/gray3"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/guide_hint_cash"
|
||||||
|
/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
|
android:id="@+id/tv_tips"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:layout_marginLeft="100dp"
|
||||||
|
android:layout_height="70dp"
|
||||||
|
android:src="@mipmap/guide_finger_b" />
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="15dp"
|
||||||
|
android:layout_marginTop="70dp"
|
||||||
|
android:padding="15dp"
|
||||||
|
android:background="@mipmap/guide_bg_left">
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/gray3"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/guide_hint_gold"
|
||||||
|
/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -32,15 +32,18 @@
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:layout_marginTop="15dp">
|
android:layout_marginTop="15dp">
|
||||||
|
|
||||||
<LinearLayout
|
<FrameLayout
|
||||||
|
android:id="@+id/gold_container"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:background="@mipmap/task_bg_glod"
|
android:background="@mipmap/task_bg_glod"
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
>
|
>
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
@ -95,15 +98,22 @@
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/cash_container"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:layout_marginLeft="10dp">
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:background="@mipmap/task_bg_cash"
|
android:background="@mipmap/task_bg_cash"
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
android:layout_marginLeft="10dp"
|
|
||||||
>
|
>
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
|
@ -158,12 +168,11 @@
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
@ -492,8 +501,6 @@
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.ama.core.architecture.widget.MyScrollView>
|
</com.ama.core.architecture.widget.MyScrollView>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
|
|
@ -149,5 +149,7 @@
|
||||||
<string name="cost_gold">Consumo de moedas</string>
|
<string name="cost_gold">Consumo de moedas</string>
|
||||||
<string name="percent">%</string>
|
<string name="percent">%</string>
|
||||||
<string name="pix2_hint">Vincular conta para saque</string>
|
<string name="pix2_hint">Vincular conta para saque</string>
|
||||||
|
<string name="guide_hint_gold">Clique no botão para converter todas \nas “Moedas” em “Dinheiro”.</string>
|
||||||
|
<string name="guide_hint_cash">Clique aqui para ir para o saque.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -0,0 +1,138 @@
|
||||||
|
package com.ama.core.architecture.highlightpro
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.ama.core.architecture.highlightpro.parameter.HighlightParameter
|
||||||
|
import com.ama.core.architecture.highlightpro.view.MaskContainer
|
||||||
|
|
||||||
|
|
||||||
|
class HighlightPro : HighlightViewInteractiveAction {
|
||||||
|
|
||||||
|
private val highlightProImpl: HighlightProImpl
|
||||||
|
|
||||||
|
private constructor(activity: Activity) {
|
||||||
|
highlightProImpl = HighlightProImpl(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(fragment: Fragment) {
|
||||||
|
highlightProImpl = HighlightProImpl(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(view: ViewGroup) {
|
||||||
|
highlightProImpl = HighlightProImpl(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [show] is a final method to call then show a guide or a popup window
|
||||||
|
*/
|
||||||
|
override fun show() {
|
||||||
|
highlightProImpl.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dismiss [HighlightPro] auto in common.
|
||||||
|
* we can manually call this method([dismiss]) to dismiss [HighlightPro]
|
||||||
|
*/
|
||||||
|
override fun dismiss() {
|
||||||
|
highlightProImpl.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set your mask background [color] of [MaskContainer]
|
||||||
|
*/
|
||||||
|
fun setBackgroundColor(color: Int): HighlightPro {
|
||||||
|
highlightProImpl.setBackgroundColor(color)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a List of [HighlightParameter] so we can show more Highlight at once
|
||||||
|
*/
|
||||||
|
fun setHighlightParameters(highlightParameters: List<HighlightParameter>): HighlightPro {
|
||||||
|
highlightProImpl.setGuideViewParameters(highlightParameters)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set single [HighlightParameter]
|
||||||
|
*
|
||||||
|
* With [setHighlightParameter] we can use kotlin function with return
|
||||||
|
*/
|
||||||
|
fun setHighlightParameter(block: ()-> HighlightParameter): HighlightPro {
|
||||||
|
highlightProImpl.setGuideViewParameter(block = block)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [showCallback] is a function which will be invoked when the [HighlightPro] show
|
||||||
|
*/
|
||||||
|
fun setOnShowCallback(showCallback: (Int) -> Unit) : HighlightPro{
|
||||||
|
highlightProImpl.setOnShowCallback(showCallback = showCallback)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [dismissCallback] is a function which will be invoked when the [HighlightPro] dismiss
|
||||||
|
*/
|
||||||
|
fun setOnDismissCallback(dismissCallback: () -> Unit) : HighlightPro{
|
||||||
|
highlightProImpl.setOnDismissCallback(dismissCallback)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [clickCallback] is a callback when user clicked any place in [MaskContainer]
|
||||||
|
*/
|
||||||
|
fun setOnMaskViewClickCallback(clickCallback: (View) -> Unit): HighlightPro {
|
||||||
|
highlightProImpl.setOnGuideViewClickCallback(clickCallback)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [enableHighlight] give you access to make Highlight dismiss and show UI like popup window
|
||||||
|
*/
|
||||||
|
fun enableHighlight(enableHighlight: Boolean): HighlightPro {
|
||||||
|
highlightProImpl.enableHighlight(enableHighlight)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [interceptBackPressed] is [true] will intercept activity onBackPressed and [HighlightPro] will dismiss
|
||||||
|
*/
|
||||||
|
fun interceptBackPressed(interceptBackPressed: Boolean): HighlightPro {
|
||||||
|
highlightProImpl.interceptBackPressed(interceptBackPressed)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun needAnchorTipView(needAnchorTipView: Boolean) : HighlightPro{
|
||||||
|
highlightProImpl.needAnchorTipView(needAnchorTipView)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DecorView of [activity] treat as the rootView
|
||||||
|
*/
|
||||||
|
fun with(activity: Activity): HighlightPro {
|
||||||
|
return HighlightPro(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DecorView of [fragment]'s [activity] treat as the rootView
|
||||||
|
*/
|
||||||
|
fun with(fragment: Fragment): HighlightPro {
|
||||||
|
return HighlightPro(fragment)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the [container] treat as rootView and the container should be a FrameLayout or a viewGroup extends FrameLayout
|
||||||
|
* to ensure the UI can display normally
|
||||||
|
*/
|
||||||
|
fun with(container: FrameLayout): HighlightPro {
|
||||||
|
return HighlightPro(container)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,226 @@
|
||||||
|
package com.ama.core.architecture.highlightpro
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.view.doOnPreDraw
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.ama.core.architecture.highlightpro.parameter.HighlightParameter
|
||||||
|
import com.ama.core.architecture.highlightpro.shape.RectShape
|
||||||
|
import com.ama.core.architecture.highlightpro.util.calculateHighLightViewRect
|
||||||
|
import com.ama.core.architecture.highlightpro.util.dp
|
||||||
|
import com.ama.core.architecture.highlightpro.util.isAttachToWindow
|
||||||
|
import com.ama.core.architecture.highlightpro.view.MaskContainer
|
||||||
|
|
||||||
|
|
||||||
|
internal class HighlightProImpl : HighlightViewInteractiveAction {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "HYY-GuideProImpl"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isFragmentRoot: Boolean = false
|
||||||
|
private var fragmentRootView: View? = null
|
||||||
|
private var curIndex: Int = 0
|
||||||
|
private val highlightParameters: MutableList<List<HighlightParameter>> = mutableListOf()
|
||||||
|
private var hasShow: Boolean = false
|
||||||
|
private val rootView: ViewGroup
|
||||||
|
private val maskContainer: MaskContainer
|
||||||
|
private var released = false
|
||||||
|
private var showCallback: ((index: Int) -> Unit)? = null
|
||||||
|
private var dismissCallback: (() -> Unit)? = null
|
||||||
|
private var clickCallback: ((View) -> Unit)? = null
|
||||||
|
private var autoNext = true
|
||||||
|
private var needAnchorTipView = true
|
||||||
|
|
||||||
|
// private var
|
||||||
|
private val onClickListener = View.OnClickListener {
|
||||||
|
clickCallback?.invoke(it)
|
||||||
|
if (autoNext) {
|
||||||
|
showNextHighLightView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(activity: Activity) {
|
||||||
|
rootView = activity.window.decorView as ViewGroup
|
||||||
|
maskContainer = MaskContainer(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(view: ViewGroup) {
|
||||||
|
rootView = view
|
||||||
|
maskContainer = MaskContainer(view.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal constructor(fragment: Fragment) {
|
||||||
|
if (fragment.view == null)
|
||||||
|
throw IllegalStateException("The fragment's view not created yet,please call this after fragment's onViewCreated()")
|
||||||
|
if (fragment.isDetached)
|
||||||
|
throw IllegalStateException("The fragment have detached. It is not attach to an activity!")
|
||||||
|
rootView = fragment.requireActivity().window.decorView as ViewGroup
|
||||||
|
fragmentRootView = fragment.view
|
||||||
|
isFragmentRoot = true
|
||||||
|
maskContainer = MaskContainer(rootView.context)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun show() {
|
||||||
|
if (released) return
|
||||||
|
println("$TAG show")
|
||||||
|
//todo give user access to intercept click event
|
||||||
|
// if (!intercept) {
|
||||||
|
maskContainer.setOnClickListener(onClickListener)
|
||||||
|
// }
|
||||||
|
|
||||||
|
//if constructor's param is activity or view we care about rootView's attachedToWindow
|
||||||
|
//if constructor's param is fragment we care about [fragmentRootView]'s width is not 0
|
||||||
|
if ((isFragmentRoot.not() && rootView.isAttachToWindow()) ||
|
||||||
|
(isFragmentRoot && fragmentRootView?.width != 0)
|
||||||
|
) {
|
||||||
|
if (maskContainer.parent == null) {
|
||||||
|
//add guideViewContainer to rootView
|
||||||
|
rootView.addView(
|
||||||
|
maskContainer,
|
||||||
|
ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (maskContainer.interceptBackPressed) {
|
||||||
|
maskContainer.apply {
|
||||||
|
//three line below enable maskContainer focusable and got focused
|
||||||
|
isFocusable = true
|
||||||
|
isFocusableInTouchMode = true
|
||||||
|
requestFocus()
|
||||||
|
setOnBackPressedCallback {
|
||||||
|
showNextHighLightView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showNextHighLightView()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isFragmentRoot) {
|
||||||
|
fragmentRootView?.doOnPreDraw {
|
||||||
|
println("$TAG fragmentRootView pre draw")
|
||||||
|
//ensure this method will be call once
|
||||||
|
if (hasShow) return@doOnPreDraw
|
||||||
|
hasShow = false
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rootView.doOnPreDraw {
|
||||||
|
//ensure this method will be call once
|
||||||
|
if (hasShow) return@doOnPreDraw
|
||||||
|
hasShow = false
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* this is the function which real show highLightView rect and tipView
|
||||||
|
*/
|
||||||
|
private fun showNextHighLightView() {
|
||||||
|
if (released) return
|
||||||
|
|
||||||
|
println("$TAG showNextHighLightView")
|
||||||
|
|
||||||
|
if (hasHighLightView().not()) {
|
||||||
|
dismiss()
|
||||||
|
} else {
|
||||||
|
highlightParameters[0].forEach {
|
||||||
|
checkOrInitParameter(it)
|
||||||
|
}
|
||||||
|
showCallback?.invoke(curIndex)
|
||||||
|
curIndex++
|
||||||
|
maskContainer.setRootWidth(rootView.width-rootView.paddingLeft-rootView.paddingRight)//ignore padding
|
||||||
|
maskContainer.setRootHeight(rootView.height-rootView.paddingTop-rootView.paddingBottom)//ignore padding
|
||||||
|
maskContainer.setHighLightParameters(highlightParameters[0])
|
||||||
|
highlightParameters.removeAt(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check valid for each parameter if invalid set default value
|
||||||
|
*/
|
||||||
|
private fun checkOrInitParameter(parameter: HighlightParameter) {
|
||||||
|
|
||||||
|
if (parameter.highLightView == null) {
|
||||||
|
parameter.highLightView = rootView.findViewById(parameter.highLightViewId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameter.tipsView == null && checkTipViewIdIsValid(parameter)) {
|
||||||
|
parameter.tipsView = LayoutInflater.from(maskContainer.context)
|
||||||
|
.inflate(parameter.tipsViewId, maskContainer, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameter.highlightShape == null) {
|
||||||
|
parameter.highlightShape = RectShape(2f.dp, 2f.dp, 2f.dp)
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter.calculateHighLightViewRect(rootView)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkTipViewIdIsValid(parameter: HighlightParameter): Boolean = parameter.tipsViewId != -1
|
||||||
|
|
||||||
|
|
||||||
|
private fun hasHighLightView(): Boolean = highlightParameters.isNotEmpty()
|
||||||
|
|
||||||
|
override fun dismiss() {
|
||||||
|
if (released) return
|
||||||
|
//release every thing
|
||||||
|
released = true
|
||||||
|
//todo if we want have a dismiss animation these code need rewrite
|
||||||
|
maskContainer.isFocusable = false
|
||||||
|
maskContainer.clearFocus()
|
||||||
|
|
||||||
|
rootView.removeView(maskContainer)
|
||||||
|
maskContainer.removeAllViews()
|
||||||
|
dismissCallback?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBackgroundColor(color: Int) {
|
||||||
|
maskContainer.setBackgroundColor(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setGuideViewParameters(highlightParameters: List<HighlightParameter>) {
|
||||||
|
if (released) return
|
||||||
|
this.highlightParameters.add(highlightParameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setGuideViewParameter(block: () -> HighlightParameter) {
|
||||||
|
if (released) return
|
||||||
|
highlightParameters.add(listOf(block.invoke()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnShowCallback(showCallback: (Int) -> Unit) {
|
||||||
|
this.showCallback = showCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnDismissCallback(dismissCallback: () -> Unit) {
|
||||||
|
this.dismissCallback = dismissCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnGuideViewClickCallback(clickCallback: (View) -> Unit) {
|
||||||
|
this.clickCallback = clickCallback
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enableHighlight(enableHighlight: Boolean) {
|
||||||
|
this.maskContainer.enableHighlight = enableHighlight
|
||||||
|
}
|
||||||
|
|
||||||
|
fun interceptBackPressed(interceptBackPressed: Boolean) {
|
||||||
|
this.maskContainer.interceptBackPressed = interceptBackPressed
|
||||||
|
}
|
||||||
|
|
||||||
|
fun needAnchorTipView(needAnchorTipView: Boolean) {
|
||||||
|
this.needAnchorTipView = needAnchorTipView
|
||||||
|
this.maskContainer.needAnchorTipView = needAnchorTipView
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ama.core.architecture.highlightpro
|
||||||
|
|
||||||
|
|
||||||
|
interface HighlightViewInteractiveAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* show [HighlightPro]
|
||||||
|
*/
|
||||||
|
fun show()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dismiss [HighlightPro]
|
||||||
|
*/
|
||||||
|
fun dismiss()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.parameter
|
||||||
|
|
||||||
|
|
||||||
|
sealed class Constraints {
|
||||||
|
//vertical constraints
|
||||||
|
object TopToTopOfHighlight : Constraints()
|
||||||
|
object BottomToTopOfHighlight : Constraints()
|
||||||
|
object BottomToBottomOfHighlight : Constraints()
|
||||||
|
object TopToBottomOfHighlight : Constraints()
|
||||||
|
//horizontal constraints
|
||||||
|
object StartToStartOfHighlight : Constraints()
|
||||||
|
object StartToEndOfHighlight : Constraints()
|
||||||
|
object EndToEndOfHighlight : Constraints()
|
||||||
|
object EndToStartOfHighlight : Constraints()
|
||||||
|
//center constraints
|
||||||
|
object CenterHorizontalOfHighlight : Constraints()
|
||||||
|
object CenterVerticalOfHighlight : Constraints()
|
||||||
|
|
||||||
|
operator fun plus(locationGravity: Constraints): List<Constraints> {
|
||||||
|
return listOf(this, locationGravity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.parameter
|
||||||
|
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.view.View
|
||||||
|
import android.view.animation.Animation
|
||||||
|
import com.ama.core.architecture.highlightpro.shape.HighlightShape
|
||||||
|
|
||||||
|
|
||||||
|
class HighlightParameter {
|
||||||
|
|
||||||
|
internal var highLightViewId: Int = -1
|
||||||
|
|
||||||
|
internal var highLightView: View? = null
|
||||||
|
|
||||||
|
internal var tipsViewId: Int = -1
|
||||||
|
|
||||||
|
internal var tipsView: View? = null
|
||||||
|
|
||||||
|
internal var highlightShape: HighlightShape? = null
|
||||||
|
|
||||||
|
internal var rect: RectF = RectF()
|
||||||
|
|
||||||
|
internal var horizontalPadding: Float = 0f
|
||||||
|
|
||||||
|
internal var verticalPadding: Float = 0f
|
||||||
|
|
||||||
|
internal var marginOffset: MarginOffset = MarginOffset()
|
||||||
|
|
||||||
|
internal var offsetX = 0
|
||||||
|
internal var offsetY = 0
|
||||||
|
|
||||||
|
internal val constraints =
|
||||||
|
mutableListOf(Constraints.TopToBottomOfHighlight, Constraints.StartToStartOfHighlight)
|
||||||
|
|
||||||
|
internal var tipViewDisplayAnimation: Animation? = null
|
||||||
|
|
||||||
|
fun setHighLightViewId(viewId: Int) {
|
||||||
|
this.highLightViewId = viewId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHighLightView(highLightView: View) {
|
||||||
|
this.highLightView = highLightView
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun plus(highlightParameter: HighlightParameter): List<HighlightParameter> {
|
||||||
|
return listOf(this, highlightParameter)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Builder {
|
||||||
|
private val highlightParameter: HighlightParameter = HighlightParameter()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [viewId] is the highLightView's id
|
||||||
|
* it will be override by [setHighlightView].
|
||||||
|
*/
|
||||||
|
fun setHighlightViewId(viewId: Int): Builder {
|
||||||
|
highlightParameter.highLightViewId = viewId
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [highLightView] is the view which you want to make it highLight
|
||||||
|
* And it will make [setHighlightViewId] useless.
|
||||||
|
*/
|
||||||
|
fun setHighlightView(highLightView: View): Builder {
|
||||||
|
highlightParameter.highLightView = highLightView
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [viewId] is the tipsView's id
|
||||||
|
* it will be override by [setTipsView].
|
||||||
|
*/
|
||||||
|
fun setTipsViewId(viewId: Int): Builder {
|
||||||
|
highlightParameter.tipsViewId = viewId
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [tipsView] is the view which you want to add it on [GuideViewContainer]
|
||||||
|
* And it will make [setTipsViewId] useless.
|
||||||
|
*/
|
||||||
|
fun setTipsView(tipsView: View): Builder {
|
||||||
|
highlightParameter.tipsView = tipsView
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [highlightShape] is the highlight out rect shape and you can
|
||||||
|
* custom your shape reference our shapes exit.
|
||||||
|
*/
|
||||||
|
fun setHighlightShape(highlightShape: HighlightShape): Builder {
|
||||||
|
highlightParameter.highlightShape = highlightShape
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [padding] is the vertical dimension padding use by expand the rect area of
|
||||||
|
* highlight.
|
||||||
|
*/
|
||||||
|
fun setHighlightVerticalPadding(padding: Float): Builder {
|
||||||
|
highlightParameter.verticalPadding = padding
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [marginOffset] is the extra dimension use by position highlight rect.
|
||||||
|
*/
|
||||||
|
fun setMarginOffset(marginOffset: MarginOffset): Builder {
|
||||||
|
highlightParameter.marginOffset = marginOffset
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [constraints] is a list of constraints provides some constraints of highlight
|
||||||
|
* rect position.
|
||||||
|
*/
|
||||||
|
fun setConstraints(constraints: List<Constraints>): Builder {
|
||||||
|
highlightParameter.constraints.clear()
|
||||||
|
highlightParameter.constraints.addAll(constraints)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [padding] is the horizontal dimension padding use by expand the rect area of
|
||||||
|
* highlight.
|
||||||
|
*/
|
||||||
|
fun setHighlightHorizontalPadding(padding: Float): Builder {
|
||||||
|
highlightParameter.horizontalPadding = padding
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [tipViewDisplayAnimation] is the animation to display for Highlight tips view
|
||||||
|
*/
|
||||||
|
fun setTipViewDisplayAnimation(tipViewDisplayAnimation: Animation?): Builder {
|
||||||
|
highlightParameter.tipViewDisplayAnimation = tipViewDisplayAnimation
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun offsetX(offsetX: Int): Builder {
|
||||||
|
highlightParameter.offsetX = offsetX
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun offsetY(offsetY: Int): Builder {
|
||||||
|
highlightParameter.offsetY = offsetY
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build(): HighlightParameter = highlightParameter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.parameter
|
||||||
|
|
||||||
|
|
||||||
|
class MarginOffset(
|
||||||
|
val start: Int = 0,
|
||||||
|
val top: Int = 0,
|
||||||
|
val end: Int = 0,
|
||||||
|
val bottom: Int = 0
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.shape
|
||||||
|
|
||||||
|
import android.graphics.Path
|
||||||
|
import android.graphics.RectF
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
|
||||||
|
class CircleShape(private val radius: Float = 0f) : HighlightShape(radius) {
|
||||||
|
|
||||||
|
override fun initRect(rectF: RectF) {
|
||||||
|
super.initRect(rectF)
|
||||||
|
rect?.run {
|
||||||
|
path.reset()
|
||||||
|
path.addCircle((left+right)/2,(top+bottom)/2, max(height(),width()) /2,Path.Direction.CW)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.shape
|
||||||
|
|
||||||
|
import android.graphics.*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
open class HighlightShape(val blurRadius: Float = 0.0f) {
|
||||||
|
|
||||||
|
private lateinit var paint: Paint
|
||||||
|
|
||||||
|
//clip path
|
||||||
|
internal val path by lazy {
|
||||||
|
Path()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
|
isDither = true
|
||||||
|
color = Color.WHITE
|
||||||
|
}
|
||||||
|
//paint blur style
|
||||||
|
if (blurRadius > 0) {
|
||||||
|
paint.maskFilter = BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.SOLID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected var rect: RectF? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* init rect child should override initRect and init path
|
||||||
|
* */
|
||||||
|
open fun initRect(rectF: RectF) {
|
||||||
|
this.rect = rectF
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* draw our path
|
||||||
|
*/
|
||||||
|
fun drawPath(canvas: Canvas) {
|
||||||
|
rect?.run {
|
||||||
|
if (isEmpty.not()) {
|
||||||
|
canvas.drawPath(path, paint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setPaint(paint: Paint) {
|
||||||
|
this.paint = paint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.shape
|
||||||
|
|
||||||
|
import android.graphics.Path
|
||||||
|
import android.graphics.RectF
|
||||||
|
|
||||||
|
|
||||||
|
class OvalShape(private val radius: Float = 0f) : HighlightShape(radius) {
|
||||||
|
|
||||||
|
override fun initRect(rectF: RectF) {
|
||||||
|
super.initRect(rectF)
|
||||||
|
rect?.run {
|
||||||
|
path.reset()
|
||||||
|
path.addOval(this,Path.Direction.CW)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.shape
|
||||||
|
|
||||||
|
import android.graphics.Path
|
||||||
|
import android.graphics.RectF
|
||||||
|
|
||||||
|
|
||||||
|
class RectShape(private val rx: Float = 0f, private val ry: Float = 0f, private val radius: Float = 0f) : HighlightShape(radius) {
|
||||||
|
|
||||||
|
override fun initRect(rectF: RectF) {
|
||||||
|
super.initRect(rectF)
|
||||||
|
rect?.run {
|
||||||
|
path.reset()
|
||||||
|
path.addRoundRect(this, rx, ry, Path.Direction.CW)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.util
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.RectF
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import com.ama.core.architecture.highlightpro.parameter.HighlightParameter
|
||||||
|
|
||||||
|
|
||||||
|
fun View.isAttachToWindow(): Boolean {
|
||||||
|
return if (Build.VERSION.SDK_INT >= 19) {
|
||||||
|
isAttachedToWindow
|
||||||
|
} else {
|
||||||
|
windowToken != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View?.getRectOnScreen(): RectF {
|
||||||
|
if (this == null) {
|
||||||
|
return RectF()
|
||||||
|
}
|
||||||
|
val pos = intArrayOf(0, 0)
|
||||||
|
getLocationOnScreen(pos)
|
||||||
|
|
||||||
|
return RectF().apply {
|
||||||
|
left = pos[0].toFloat()
|
||||||
|
top = pos[1].toFloat()
|
||||||
|
right = pos[0].toFloat() + width
|
||||||
|
bottom = pos[1].toFloat() + height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun HighlightParameter.calculateHighLightViewRect(rootView: ViewGroup) {
|
||||||
|
if (highLightView == null) return
|
||||||
|
val rectOnScreen = highLightView.getRectOnScreen()
|
||||||
|
|
||||||
|
//consider rootView's position
|
||||||
|
val rootViewPos = IntArray(2)
|
||||||
|
rootView.getLocationOnScreen(rootViewPos)
|
||||||
|
rectOnScreen.left -= rootViewPos[0]
|
||||||
|
rectOnScreen.right -= rootViewPos[0]
|
||||||
|
rectOnScreen.top -= rootViewPos[1]
|
||||||
|
rectOnScreen.bottom -= rootViewPos[1]
|
||||||
|
|
||||||
|
rectOnScreen.left -= rootView.paddingLeft.toFloat()
|
||||||
|
rectOnScreen.right -= rootView.paddingLeft.toFloat()
|
||||||
|
rectOnScreen.top -= rootView.paddingTop.toFloat()
|
||||||
|
rectOnScreen.bottom -= rootView.paddingTop.toFloat()
|
||||||
|
rect = rectOnScreen
|
||||||
|
rect.run {
|
||||||
|
left -= horizontalPadding
|
||||||
|
top -= verticalPadding
|
||||||
|
right += horizontalPadding
|
||||||
|
bottom += verticalPadding
|
||||||
|
|
||||||
|
highlightShape?.initRect(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val Float.dp
|
||||||
|
get() = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
this,
|
||||||
|
Resources.getSystem().displayMetrics
|
||||||
|
)
|
||||||
|
|
||||||
|
val Float.sp
|
||||||
|
get() = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_SP,
|
||||||
|
this,
|
||||||
|
Resources.getSystem().displayMetrics
|
||||||
|
)
|
||||||
|
|
||||||
|
val Int.dp
|
||||||
|
get() = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
this.toFloat(),
|
||||||
|
Resources.getSystem().displayMetrics
|
||||||
|
).toInt()
|
||||||
|
|
||||||
|
val Int.sp
|
||||||
|
get() = TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_SP,
|
||||||
|
this.toFloat(),
|
||||||
|
Resources.getSystem().displayMetrics
|
||||||
|
).toInt()
|
||||||
|
|
@ -0,0 +1,262 @@
|
||||||
|
package com.ama.core.architecture.highlightpro.view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Region
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import androidx.core.graphics.toColorInt
|
||||||
|
import androidx.core.view.children
|
||||||
|
import androidx.core.view.doOnPreDraw
|
||||||
|
import com.ama.core.architecture.highlightpro.parameter.Constraints
|
||||||
|
import com.ama.core.architecture.highlightpro.parameter.HighlightParameter
|
||||||
|
|
||||||
|
|
||||||
|
internal class MaskContainer constructor(context: Context, attributeSet: AttributeSet? = null) :
|
||||||
|
FrameLayout(context, attributeSet) {
|
||||||
|
companion object {
|
||||||
|
const val TAG = "MaskContainer"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var rootWidth: Int = 0
|
||||||
|
private var rootHeight: Int = 0
|
||||||
|
private var bgColor: Int = -1
|
||||||
|
private val highLightViewParameters = mutableListOf<HighlightParameter>()
|
||||||
|
private val defaultHighlightBgColor: Int
|
||||||
|
get() = "#80000000".toColorInt()
|
||||||
|
|
||||||
|
private val defaultBgColor: Int
|
||||||
|
get() = "#00000000".toColorInt()
|
||||||
|
|
||||||
|
private var backPressedCallback: (() -> Unit)? = null
|
||||||
|
internal var enableHighlight = true
|
||||||
|
internal var interceptBackPressed = false
|
||||||
|
internal var needAnchorTipView = true
|
||||||
|
|
||||||
|
init {
|
||||||
|
setWillNotDraw(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas) {
|
||||||
|
super.onDraw(canvas)
|
||||||
|
if (enableHighlight) {
|
||||||
|
canvas.save()
|
||||||
|
|
||||||
|
//clip rect
|
||||||
|
highLightViewParameters.forEach { parameter ->
|
||||||
|
parameter.highlightShape?.run {
|
||||||
|
canvas.clipPath(path, Region.Op.DIFFERENCE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bgColor == -1) {
|
||||||
|
bgColor = defaultHighlightBgColor
|
||||||
|
}
|
||||||
|
canvas.drawColor(bgColor)
|
||||||
|
|
||||||
|
highLightViewParameters.forEach { parameter ->
|
||||||
|
parameter.highlightShape?.run {
|
||||||
|
drawPath(canvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canvas.restore()
|
||||||
|
} else {
|
||||||
|
if (bgColor == -1) {
|
||||||
|
bgColor = defaultBgColor
|
||||||
|
}
|
||||||
|
canvas.drawColor(bgColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setBackgroundColor(color: Int) {
|
||||||
|
// super.setBackgroundColor(color)
|
||||||
|
bgColor = color
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnBackPressedCallback(block: () -> Unit) {
|
||||||
|
backPressedCallback = block
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
|
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
backPressedCallback?.invoke()
|
||||||
|
return interceptBackPressed
|
||||||
|
}
|
||||||
|
return super.onKeyDown(keyCode, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHighLightParameters(list: List<HighlightParameter>) {
|
||||||
|
children.forEach {
|
||||||
|
it.clearAnimation()
|
||||||
|
}
|
||||||
|
//clear all childView
|
||||||
|
removeAllViews()
|
||||||
|
|
||||||
|
highLightViewParameters.clear()
|
||||||
|
highLightViewParameters.addAll(list)
|
||||||
|
|
||||||
|
addTipsView()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addTipsView() {
|
||||||
|
if (needAnchorTipView) {
|
||||||
|
highLightViewParameters.forEach { highLightViewParameters ->
|
||||||
|
highLightViewParameters.tipsView?.run {
|
||||||
|
val layoutParams = calculateTipsViewLayoutParams(this, highLightViewParameters)
|
||||||
|
if (highLightViewParameters.tipViewDisplayAnimation != null) {
|
||||||
|
startAnimation(highLightViewParameters.tipViewDisplayAnimation)
|
||||||
|
}
|
||||||
|
addView(this, layoutParams)
|
||||||
|
highLightViewParameters.tipsView?.doOnPreDraw {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
highLightViewParameters.forEach { highLightViewParameters ->
|
||||||
|
highLightViewParameters.tipsView?.run {
|
||||||
|
var layoutParams = (this.layoutParams ?: LayoutParams(
|
||||||
|
LayoutParams.WRAP_CONTENT,
|
||||||
|
LayoutParams.WRAP_CONTENT
|
||||||
|
)) as LayoutParams
|
||||||
|
layoutParams.topMargin = highLightViewParameters.offsetY
|
||||||
|
layoutParams.leftMargin = highLightViewParameters.offsetX
|
||||||
|
if (highLightViewParameters.tipViewDisplayAnimation != null) {
|
||||||
|
startAnimation(highLightViewParameters.tipViewDisplayAnimation)
|
||||||
|
}
|
||||||
|
addView(this, layoutParams)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateTipsViewLayoutParams(
|
||||||
|
view: View,
|
||||||
|
parameter: HighlightParameter
|
||||||
|
): LayoutParams {
|
||||||
|
var layoutParams = (view.layoutParams ?: LayoutParams(
|
||||||
|
LayoutParams.WRAP_CONTENT,
|
||||||
|
LayoutParams.WRAP_CONTENT
|
||||||
|
)) as LayoutParams
|
||||||
|
|
||||||
|
println("$TAG calculateTipsViewLayoutParams tipsView layoutParams--> $layoutParams")
|
||||||
|
val margin = parameter.marginOffset
|
||||||
|
val highLightRect = parameter.rect
|
||||||
|
val gravities = mutableListOf<Int>()
|
||||||
|
parameter.constraints.forEach {
|
||||||
|
when (it) {
|
||||||
|
Constraints.StartToStartOfHighlight -> {
|
||||||
|
layoutParams.leftMargin = (highLightRect.left + margin.start).toInt()
|
||||||
|
gravities.add(Gravity.START)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.EndToStartOfHighlight -> {
|
||||||
|
//if constraint start to end of Highlight we need set right rightMargin
|
||||||
|
//and if margin.end is not 0 we should add the margin end
|
||||||
|
layoutParams.rightMargin =
|
||||||
|
(rootWidth - highLightRect.right + highLightRect.width() + margin.end).toInt()
|
||||||
|
gravities.add(Gravity.END)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.StartToEndOfHighlight -> {
|
||||||
|
layoutParams.leftMargin = (highLightRect.right + margin.start).toInt()
|
||||||
|
gravities.add(Gravity.START)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.EndToEndOfHighlight -> {
|
||||||
|
layoutParams.rightMargin =
|
||||||
|
(rootWidth - highLightRect.right + margin.end).toInt()
|
||||||
|
gravities.add(Gravity.END)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.TopToTopOfHighlight -> {
|
||||||
|
layoutParams.topMargin = (highLightRect.top + margin.top).toInt()
|
||||||
|
gravities.add(Gravity.TOP)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.BottomToBottomOfHighlight -> {
|
||||||
|
layoutParams.bottomMargin =
|
||||||
|
(rootHeight - highLightRect.bottom + margin.bottom).toInt()
|
||||||
|
gravities.add(Gravity.BOTTOM)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.BottomToTopOfHighlight -> {
|
||||||
|
layoutParams.bottomMargin =
|
||||||
|
(rootHeight - highLightRect.bottom + highLightRect.height() + margin.bottom).toInt()
|
||||||
|
gravities.add(Gravity.BOTTOM)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.TopToBottomOfHighlight -> {
|
||||||
|
layoutParams.topMargin = (highLightRect.bottom + margin.top).toInt()
|
||||||
|
gravities.add(Gravity.TOP)
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.CenterHorizontalOfHighlight -> {
|
||||||
|
val width = layoutParams.width
|
||||||
|
|
||||||
|
if (width <= 0) {
|
||||||
|
layoutParams.leftMargin =
|
||||||
|
(highLightRect.left + highLightRect.width() / 2f).toInt()
|
||||||
|
gravities.add(Gravity.START)
|
||||||
|
view.doOnPreDraw { tipsView ->
|
||||||
|
layoutParams.leftMargin =
|
||||||
|
(highLightRect.left + highLightRect.width() / 2f - tipsView.width / 2f).toInt()
|
||||||
|
view.layoutParams = layoutParams
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
layoutParams.leftMargin =
|
||||||
|
(highLightRect.left + highLightRect.width() / 2f - width / 2f).toInt()
|
||||||
|
gravities.add(Gravity.START)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Constraints.CenterVerticalOfHighlight -> {
|
||||||
|
val height = layoutParams.height
|
||||||
|
if (height <= 0) {
|
||||||
|
layoutParams.topMargin =
|
||||||
|
(highLightRect.top + highLightRect.height() / 2f).toInt()
|
||||||
|
gravities.add(Gravity.TOP)
|
||||||
|
view.doOnPreDraw { tipsView ->
|
||||||
|
layoutParams.topMargin =
|
||||||
|
(highLightRect.top + highLightRect.height() / 2f - tipsView.height / 2f).toInt()
|
||||||
|
view.layoutParams = layoutParams
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
layoutParams.topMargin =
|
||||||
|
(highLightRect.top + highLightRect.height() / 2f - height / 2f).toInt()
|
||||||
|
gravities.add(Gravity.TOP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gravities.forEachIndexed { index, gravity ->
|
||||||
|
if (index == 0) layoutParams.gravity = gravity
|
||||||
|
else layoutParams.gravity = layoutParams.gravity or gravity
|
||||||
|
}
|
||||||
|
return layoutParams
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromWindow() {
|
||||||
|
super.onDetachedFromWindow()
|
||||||
|
highLightViewParameters.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setRootWidth(width: Int) {
|
||||||
|
this.rootWidth = width
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setRootHeight(height: Int) {
|
||||||
|
this.rootHeight = height
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue