개발포스팅은 언제나 유통기한을 조심하세요.

 

 

Github : https://github.com/junghun9102/AndroidTemplate

Branch : ui/custom_dialog

 

junghun9102/AndroidTemplate

Contribute to junghun9102/AndroidTemplate development by creating an account on GitHub.

github.com

 

Activity 외에 필수적으로 사용하게 되는 Toast, Dialog 등이 있다.

앱 디자인에 더 완벽하게 맞추고 싶을 수 있다.

 

커스텀해서 사용해보자!

 

 

 


CustomToast

 

 

 

 

view_toast.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_view_toast"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:padding="15dp"
        android:background="#7000"
        android:gravity="center"
        android:textColor="@android:color/white"/>

</LinearLayout>

 

원하는 토스트 레이아웃을 만든다

 

 

 

CustomToast.kt

object CustomToast {

    fun show(context: Context, stringResId: Int, duration: Int = Toast.LENGTH_SHORT) = show(context, context.getString(stringResId), duration)

    private fun show(context: Context, content: String, duration: Int = Toast.LENGTH_SHORT) {
        val view = LayoutInflater.from(context).inflate(R.layout.view_toast, null)
        view.tv_view_toast.text = content

        Toast(context).apply {
            setGravity(
                Gravity.FILL_HORIZONTAL or Gravity.BOTTOM,
                0,
                context.dimen(R.dimen.custom_toast_bottom_margin)
            )
            this.duration = duration
            setView(view)
        }.show()
    }

}

 

dimen에 bottomMargin을 정해두고 사용했다.

(혹시 모르시는 분이 있을까봐 context.dimen은 anko 라이브러리)

 

토스트 내용은 하드코딩하지 않는 한 resource 레퍼런스만

받아서 사용하기 때문에 String으로 받는 함수는 private으로 묶어 두었다.

 

 

 

 

ContextExtensions.kt

fun Context.showToast(resId: Int) = CustomToast.show(this, resId)

 

MainActivity.kt

    private fun initViews() {
        btn_main_toast.setOnClickListener {
            showToast(R.string.app_name)
        }
    }

 

사용하기 쉽도록 extension을 추가했다.

 

 

 


CustomDialog

 

 

 

 

원하는 레이아웃형태 만들기

view_dialog.xml

더보기
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <TextView
        android:id="@+id/tv_view_dialog_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:textSize="20dp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tv_view_dialog_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="5dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="20dp"
        android:gravity="end">

        <TextView
            android:id="@+id/tv_view_dialog_negative"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:padding="5dp"
            android:foreground="?attr/selectableItemBackground"
            android:text="@string/common_cancel" />

        <TextView
            android:id="@+id/tv_view_dialog_positive"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:foreground="?attr/selectableItemBackground"
            android:text="@string/common_confirm" />

    </LinearLayout>
    
</LinearLayout>

 

 

 

 

CustomDialog.kt

class CustomDialog(
    context: Context,
    title: String,
    content: String? = null
) {

    private val view = LayoutInflater.from(context).inflate(R.layout.view_dialog, null)

    private val dialog = AlertDialog.Builder(context)
        .setView(view)
        .create()

    fun show() {
        dialog.show()
    }
    
    fun setPositiveButton(buttonTextResId: Int, func: () -> Unit = defaultClickFunc) =
        setPositiveButton(view.context.getString(buttonTextResId), func)
    
    /*
        이하 코드생략
    */
    
}

 

전체코드 보기

더보기
class CustomDialog(
    context: Context,
    title: String,
    content: String? = null
) {

    constructor(context: Context, titleResId: Int, contentResId: Int? = null) : this(
        context,
        context.getString(titleResId),
        contentResId?.let { context.getString(it) }
    )

    private val view = LayoutInflater.from(context).inflate(R.layout.view_dialog, null)
    private val titleView = view.tv_view_dialog_title
    private val contentView = view.tv_view_dialog_content
    private val positiveView = view.tv_view_dialog_positive
    private val negativeView = view.tv_view_dialog_negative
    private val defaultClickFunc = { dialog.dismiss() }

    init {
        titleView.text = title
        content?.let {
            contentView.text = it
        } ?: kotlin.run {
            contentView.visibility = View.GONE
        }

        positiveView.setOnClickListener { defaultClickFunc.invoke() }
        negativeView.setOnClickListener { defaultClickFunc.invoke() }
    }

    private val dialog = AlertDialog.Builder(context)
        .setView(view)
        .create()

    fun show() {
        dialog.show()
    }

    fun setPositiveButton(buttonText: String, func: () -> Unit = defaultClickFunc): CustomDialog {
        setTextViewForButton(positiveView, buttonText, func)
        return this
    }

    fun setPositiveButton(buttonTextResId: Int, func: () -> Unit = defaultClickFunc) =
        setPositiveButton(view.context.getString(buttonTextResId), func)

    fun setNegativeButton(buttonText: String, func: () -> Unit = defaultClickFunc): CustomDialog {
        setTextViewForButton(negativeView, buttonText, func)
        return this
    }

    fun setNegativeButton(buttonTextResId: Int, func: () -> Unit = defaultClickFunc) =
        setNegativeButton(view.context.getString(buttonTextResId), func)

    private fun setTextViewForButton(view: TextView, buttonText: String, func: () -> Unit) {
        view.run {
            text = buttonText
            setOnClickListener {
                func.invoke()
                dialog.dismiss()
            }
        }
    }

}

 

 

MainActivity.kt

    private fun initViews() {
        btn_main_alert_dialog.setOnClickListener {
            CustomDialog(this, R.string.dialog_delete_title, R.string.dialog_delete_content)
                .setPositiveButton(R.string.common_delete) {
                    showToast(R.string.message_delete_complete)
                }
                .show()
        }
    }

 

핵심은 인플레이팅한 커스텀 뷰를 AlertDialog에 setView하는 것이다.

 

기존의 AlertDialog를 대체할 Dialog를 만들었지만

전혀 다른 방식으로 간단한 선택창 또는 도움창 등을 보여주는데 사용할 수 있다.

 

 

 

 

 

'창고' 카테고리의 다른 글

Retrofit / OkHttp  (0) 2020.05.29
Activity간의 화면 전환  (0) 2020.03.11
internal_storage  (0) 2020.02.26
permission  (0) 2020.02.26
DP(Device Independence Pixel)?  (0) 2019.06.09

 

WARNING

 

 

App is not indexable by Google Search; consider adding at least one Activity with an ACTION-VIEW intent filter. See issue explanation for more details.

inspection info:Adds URLs to get your app into the Google index; to get installs and traffic to your app from Google Search

 

issue id GoogleAppIndexingWarning

 

More info:

https://g.co/Appindexing/AndroidStudio

 

 

 


WHY

 

링크를 들어가 보면 앱 링크 추가에 관한 내용이 나온다.

 

앱 링크란 앱의 특정 컨텐츠로 사용자를 바로 안내하는 HTTP URL이다.

해당 기능을 통해 앱에 더 접근하기 쉽고 더 많은 유입을 이끌어 낼 수 있다.

 

다만 웹과 앱을 동시에 보유한 프로젝트에 한해 가능해 보인다.

 

여기서 구글 검색에서 해당 컨텐츠에 도달하기 위해 ACTION_VIEW 인텐트가 필요하다.

기본 설정으로 최소 하나 이상의 Activity에 ACTION_VIEW 인텐트 필터가 필요하다.

 

 

 


SOLUTION

 

 

<action android: name="android.intent.action.VIEW" />

위 코드를 한 개 이상의 Activity에 추가하면 warning을 해결할 수 있다.

 

 

 


ADVANCE

 

앱 링크에 대해 더 자세히 알고 싶다면...

 

https://developer.android.com/training/app-links

 

Android 앱 링크 처리하기  |  Android 개발자  |  Android Developers

기기에서 웹 링크를 따라가는 사용자는 종종 혼란스러운 선택에 직면합니다. 링크를 탭하면 시스템에서 사용자에게 링크를 처리할 앱을 지정해 줄 것을 요청하는 경우가 자주 있습니다. 예를 들어 은행에서 온 이메일에 포함된 URI를 클릭할 때 다음과 같은 대화상자가 열릴 수 있습니다.

developer.android.com

 

https://developer.android.com/training/app-links/deep-linking

 

앱 콘텐츠 딥 링크 만들기  |  Android 개발자  |  Android Developers

사용자가 링크에서 앱에 진입할 수 있도록 하려면 관련 활동의 인텐트 필터를 앱 manifest에 추가해야 합니다. 이러한 인텐트 필터는 모든 활동의 콘텐츠로 연결되는 딥 링크를 허용…

developer.android.com

 

https://developer.android.com/studio/write/app-link-indexing

 

Android 앱 링크 추가  |  Android 개발자  |  Android Developers

Android 앱 링크는 Android 앱의 특정 콘텐츠로 사용자를 바로 안내하는 HTTP URL입니다.

developer.android.com

 

 

 

 

 

overridePendingTransition(int enterAnim, int exitAnim)

Call immediately after one of the flavors of startActivity(android.content.Intent) or finish() to specify an explicit transition animation to perform next.

 

 

1. Activity가 실행, 종료 코드 직후 바로 호출하면된다.

2. 첫번째 파라미터로 보여질 화면의 애니메이션을 입력한다.

3. 두번째 파라미터로 종료되거나 가려질 화면의 애니메이션을 입력한다.

 

 

 

 


 

MainActivity의 왼쪽에서 LeftActivity가 들어온다.

 

 

MainActivity.kt

        btn_main_left.setOnClickListener {
            startActivity(Intent(this@MainActivity, LeftActivity::class.java))
            overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
        }

 

1. startActivity 후에 overridePendingTransition을 호출한다.

2. LeftActivity는 왼쪽에서 들어와야 하기 때문에 slide_in_left를 넘겨준다.

3. MainActivity는 오른쪽으로 나가야 하기 때문에 slide_out_right를 넘겨준다.

 

 

 

slide_in_left.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="-100%p" android:toXDelta="0"
        android:duration="@android:integer/config_mediumAnimTime"/>
</set>

 

slide_out_right.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0" android:toXDelta="100%p"
        android:duration="@android:integer/config_mediumAnimTime"/>
</set>

 

 

 

 

 


 

종료시에는 반대로

MainActivity가 오른쪽에서 들어오고 LeftActivity가 왼쪽으로 나가는 것이 자연스럽다.

 

 

LeftActivity.kt

    override fun finish() {
        super.finish()
        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)
    }

 

1. finish 후에 overridePendingTransition을 호출한다.

2. MainActivity는 오른쪽에서 들어와야 하기 때문에 slide_in_right를 넘겨준다.

3. LeftActivity는 왼쪽으로 나가야 하기 때문에 slide_out_left를 넘겨준다.

 

 

 

slide_in_right.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="100%p" android:toXDelta="0"
        android:duration="@android:integer/config_mediumAnimTime"/>
</set>

 

slide_out_left.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate android:fromXDelta="0" android:toXDelta="-100%p"
        android:duration="@android:integer/config_mediumAnimTime"/>
</set>

 

 

 

 

 


 

위에 예시를 포함한

상황에 맞는 애니메이션을 사용하면 된다.

 

fromXDelta, toXDelta를 fromYDelta, toTDelta로 변경하면 위아래로 이동할 수 있고

android.R.anim.fade_in, android.R.anim.fade_out을 사용할 수도 있다.

 

 

 

 

 

'창고' 카테고리의 다른 글

Retrofit / OkHttp  (0) 2020.05.29
custom_dialog (+custom_toast)  (0) 2020.05.02
internal_storage  (0) 2020.02.26
permission  (0) 2020.02.26
DP(Device Independence Pixel)?  (0) 2019.06.09

+ Recent posts