初始版本:

支持:设置文本标题
支持:设置自定义View标题
支持:设置关闭按钮
支持:设置文本正文
支持:设置自定义View
支持:标题栏上下滑动来扩展
This commit is contained in:
Yutousama 2022-05-25 22:56:45 +08:00
parent fec4d98691
commit c59516aa2a
26 changed files with 641 additions and 11 deletions

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
@ -10,6 +11,7 @@
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/POPDialog" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>

View File

@ -1,5 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DesignSurface">
<option name="filePathToZoomLevelMap">
<map>
<entry key="..\:/AndroidStudio_Data/POPUPDialog/POPDialog/src/main/res/drawable/dialog_line.xml" value="0.2125" />
<entry key="..\:/AndroidStudio_Data/POPUPDialog/POPDialog/src/main/res/drawable/dialog_title.xml" value="0.181" />
<entry key="..\:/AndroidStudio_Data/POPUPDialog/POPDialog/src/main/res/layout/dialog.xml" value="0.22" />
<entry key="..\:/AndroidStudio_Data/POPUPDialog/app/src/main/res/layout/activity_main.xml" value="0.20520833333333333" />
</map>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>

1
POPDialog/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

39
POPDialog/build.gradle Normal file
View File

@ -0,0 +1,39 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 32
defaultConfig {
minSdk 23
targetSdk 32
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
}

View File

21
POPDialog/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package com.yutou.popdialog
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.yutou.popdialog.test", appContext.packageName)
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yutou.popdialog">
</manifest>

View File

@ -0,0 +1,280 @@
package com.yutou.popdialog
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog
import android.graphics.Color
import android.view.*
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.GestureDetectorCompat
import com.yutou.popdialog.utils.POPDialogUtils
import com.yutou.popdialog.views.ExitButton
class POPDialog(context: Activity) : Dialog(context) {
private val activityContext: Activity = context
private lateinit var title: TextView
private val layout: FrameLayout by lazy {
findViewById(R.id.title)
}
private val dialogContext: FrameLayout by lazy {
findViewById(R.id.context)
}
private lateinit var view: View
private lateinit var mainView: FrameLayout
private lateinit var touch: GestureDetectorCompat
private var touchHook = false
private var showModel = 0
private var expansionY = 0f
init {
requestWindowFeature(Window.FEATURE_NO_TITLE)
initView()
}
private fun initView() {
mainView = FrameLayout(activityContext)
view =
LayoutInflater.from(context).inflate(R.layout.dialog, mainView, false) as LinearLayout
val animation = AnimationUtils.loadAnimation(
context,
R.anim.enter_anim
)
view.startAnimation(animation)
val windowParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
windowParams.gravity = Gravity.BOTTOM
view.layoutParams = windowParams
mainView.addView(view)
super.setContentView(mainView)
}
/**
* 设置标题文本
* @param title 内容
*/
override fun setTitle(title: CharSequence?) {
this@POPDialog.title = TextView(ContextThemeWrapper(activityContext, R.style.DialogTitle))
this@POPDialog.title.text = title
layout.addView(this@POPDialog.title)
}
/**
* 设置标题View
* @param view 内容
*/
fun setTitle(view: View) {
layout.addView(view)
}
/**
* 设置正文文本
* @param text 内容
*/
public fun setMessage(text: String) {
val textView = TextView(ContextThemeWrapper(activityContext, R.style.messageText))
val params = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
);
textView.layoutParams = params;
textView.text = text
dialogContext.addView(textView)
}
/**
* 启用关闭按钮
* 默认false
*/
fun enableExitButton(enable: Boolean) {
if (enable) {
val exitButton = ExitButton(activityContext)
val params = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
params.gravity = Gravity.RIGHT
exitButton.layoutParams = params
exitButton.setOnClickListener {
if (showModel == 1) {
expansion(false)
} else {
finish()
}
}
layout.addView(exitButton)
}
}
/**
* 自定义正文view
* @param view 正文
*/
override fun setContentView(view: View) {
dialogContext.addView(view)
}
/**
* 不允许设置资源id
* @param layoutResID 正文id
*/
@Deprecated("设置资源id会导致内容无法控制不建议使用",
ReplaceWith("throw RuntimeException")
)
override fun setContentView(layoutResID: Int) {
throw RuntimeException(context.resources.getString(R.string.pop_dialog_exception_disable_content_layoutId))
}
/**
* 配置Dialog参数
*/
private fun configDialog() {
val display: Display = POPDialogUtils.getDisplay(activityContext)
window!!.setLayout(
WindowManager.LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.MATCH_PARENT
)
window!!.setWindowAnimations(R.style.dialog_pop_anim)
val attributes = window?.attributes!!
attributes.windowAnimations = R.anim.enter_anim
attributes.x = 0
attributes.y = 0
window!!.attributes = attributes
window!!.decorView.minimumHeight = display.mode.physicalHeight
window!!.decorView.minimumWidth = display.mode.physicalWidth
window!!.decorView.setBackgroundColor(Color.parseColor("#10000000"))
window!!.decorView.setPadding(0, 0, 0, 0)
}
/**
* 展开对话框
* @param expansion true 展开 false 拉下
*/
private fun expansion(expansion: Boolean) {
val viewAnim = if (expansion) {
ObjectAnimator.ofFloat(view, "translationY", 0f).setDuration(300L)
} else {
ObjectAnimator.ofFloat(view, "translationY", expansionY).setDuration(300L)
}
showModel = if (expansion) {
1
} else {
0
}
viewAnim.start()
}
/**
* 获取正文view所占用大小
*/
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (hasFocus) {
if (dialogContext.height > (POPDialogUtils.getDisplay(activityContext).mode.physicalHeight / 3)) {
touchHook = true
val params = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
params.height = (dialogContext.height / 1.25).toInt()
dialogContext.layoutParams = params
view.y =(POPDialogUtils.getDisplay(activityContext).mode.physicalHeight / 2).toFloat()
expansionY = view.y
}
configTouch()
}
}
/**
* 配置触摸事件
*/
@SuppressLint("ClickableViewAccessibility")
private fun configTouch() {
val layout = FrameLayout(activityContext)
val params = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
params.width= (POPDialogUtils.getDisplay(activityContext).mode.physicalWidth*0.8).toInt()
params.gravity=Gravity.CENTER
layout.layoutParams = params
layout.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(p0: View?, p1: MotionEvent?): Boolean {
if (touchHook) {
touch.onTouchEvent(p1)
return true
}
return false
}
})
this@POPDialog.layout.addView(layout)
}
/**
* 显示弹窗
*/
override fun show() {
configDialog()
super.show()
touch = GestureDetectorCompat(activityContext, DialogTouch())
}
/**
* 结束弹窗
*/
fun finish() {
val animation = AnimationUtils.loadAnimation(
context,
R.anim.exit_anim
)
animation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
dismiss()
}
override fun onAnimationRepeat(p0: Animation?) {
}
})
view.startAnimation(animation)
}
/**
* 触摸事件
*/
private inner class DialogTouch : GestureDetector.SimpleOnGestureListener() {
override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent?,
distanceX: Float,
distanceY: Float
): Boolean {
super.onScroll(e1, e2, distanceX, distanceY)
if ((e2!!.y - e1!!.y) < -100f) {
expansion(true)
return true
} else if ((e2.y - e1.y) > 100f) {
expansion(false)
return true
}
return false
}
}
}

View File

@ -0,0 +1,20 @@
package com.yutou.popdialog.utils
import android.app.Activity
import android.os.Build
import android.view.Display
class POPDialogUtils {
companion object{
/**
* 获取Display
*/
fun getDisplay(context:Activity): Display {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
context.display!!
} else {
context.windowManager.defaultDisplay
}
}
}
}

View File

@ -0,0 +1,51 @@
package com.yutou.popdialog.views
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.view.View
import kotlin.properties.Delegates
class ExitButton(context:Context): View(context) {
private var buttonColor by Delegates.notNull<Int>()
private var buttonBackgroundColor =Color.WHITE
init {
buttonColor= Color.parseColor("#AEB8B9")
}
override fun draw(canvas: Canvas?) {
super.draw(canvas)
val shadowPaint = Paint()
shadowPaint.isAntiAlias = true
shadowPaint.style = Paint.Style.FILL
shadowPaint.color = buttonBackgroundColor
val shadow = RectF(9f, 9f, 71f, 71f)
canvas!!.drawArc(shadow, 0f, 360f, true, shadowPaint)
val paint = Paint()
paint.isAntiAlias = true
paint.color = buttonBackgroundColor
paint.style = Paint.Style.FILL
paint.strokeWidth = 3f
val rectF = RectF(10f, 10f, 70f, 70f)
canvas.drawArc(rectF, 0f, 360f, true, paint)
val linePaint = Paint()
linePaint.isAntiAlias = true
linePaint.color = buttonColor
linePaint.style = Paint.Style.FILL
linePaint.strokeWidth = 3f
val lineStart = Vector2(25f, 25f)
val lineEnd = Vector2(55f, 55f)
canvas.drawLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, linePaint)
canvas.drawLine(lineEnd.x, lineStart.y, lineStart.y, lineEnd.x, linePaint)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(80,80)
}
private class Vector2 constructor(var x: Float, var y: Float)
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:fillBefore="false"
android:duration="300">
<translate
android:fromXDelta="0"
android:fromYDelta="+50%p"
android:toXDelta="0"
android:toYDelta="0" />
</set>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:fillBefore="false"
android:duration="300">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="+60%p" />
</set>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#9e9e9e"/>
<stroke android:dashGap="1dp" android:dashWidth="1dp"/>
</shape>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:topRightRadius="30dp" android:topLeftRadius="30dp"/>
<solid android:color="#f1f2f6"/>
</shape>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/linearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/dialog_title"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<FrameLayout
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="10dp"
android:layout_weight="1"
android:orientation="horizontal" />
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
app:layout_constraintTop_toBottomOf="@id/linearLayout2"
android:background="?android:attr/listDivider" />
<FrameLayout
android:id="@+id/context"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#f1f2f6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/divider"
android:orientation="vertical" />
</LinearLayout>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pop_dialog_exception_disable_content_layoutId">禁止使用资源id来填充正文部分</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pop_dialog_exception_disable_content_layoutId">Disallow padding the body section with layoutId</string>
</resources>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="SuspensionDialog">
<item name="android:background">@drawable/dialog_title</item>
</style>
<style name="BaseDialog" parent="Theme.AppCompat.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsFloating">false</item>
</style>
<style name="DialogTitle">
<item name="android:textSize">15sp</item>
<item name="android:textColor">#212121</item>
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center</item>
</style>
<style name="dialog_pop_anim" parent="android:Animation" mce_bogus="1">
<item name="android:windowEnterAnimation">@anim/enter_anim</item>
</style>
<style name="messageText">
<item name="android:textColor">#212121</item>
<item name="android:gravity">center</item>
<item name="android:padding">20dp</item>
</style>
</resources>

View File

@ -0,0 +1,17 @@
package com.yutou.popdialog
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@ -13,7 +13,7 @@ android {
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
@ -33,9 +33,10 @@ android {
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation project(path:':POPDialog')
}

View File

@ -1,7 +1,7 @@
package com.yutou.pop_up_dialog
import android.support.test.InstrumentationRegistry
import android.support.test.runner.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith

View File

@ -1,11 +1,52 @@
package com.yutou.pop_up_dialog
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.FrameLayout
import android.widget.ListView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.yutou.popdialog.POPDialog
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.dialog_textView)
textView.setOnClickListener {
showDialog()
}
showDialog()
}
fun showDialog() {
val params = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 300)
val textView = TextView(this@MainActivity)
textView.text = "aaaaaaaaaaaaaa"
textView.layoutParams = params
val dialog = POPDialog(this)
//dialog.setTitle(textView)
dialog.setTitle("标题")
//dialog.setMessage("正文测试")
//dialog.setContentView(textView)
dialog.setContentView(getListView())
dialog.enableExitButton(true)
dialog.show()
}
private fun getListView(): ListView {
val listView = ListView(this@MainActivity)
val list = ArrayList<String>()
for (i in 1..50) {
list.add("item is $i")
}
listView.adapter =
ArrayAdapter<String>(this@MainActivity, android.R.layout.simple_list_item_1, list)
return listView
}
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
@ -7,6 +7,7 @@
tools:context=".MainActivity">
<TextView
android:id="@+id/dialog_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
@ -15,4 +16,5 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -17,3 +17,5 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
android.useAndroidX=true
android.enableJetifier=true

View File

@ -14,3 +14,4 @@ dependencyResolutionManagement {
}
rootProject.name = "POP-UP-Dialog"
include ':app'
include ':POPDialog'