은행 App을 사용할 때보면 숫자입력 키 패드가 나올때마다 숫자의 위치가 변경되어 나타나는 것을 알 수 있습니다.
오늘은 그 기능을 구현해 보려합니다.
작성 Code는 kotlin 입니다.
우선 xml 부터 작성해 보겠습니다.
구성은 아래와 같습니다.
맨 위에 edit 와 총 12개의 숫자 버튼이 있고 비어있는 부분에는 숫자들이 랜덤으로 들어 갑니다. 그리고 중간에는 Textview가 있는데 이곳에 결과를 노출할 것입니다.
우선 버튼에 대한 drawable 관련 code를 보겠습니다.
숫자버튼
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<gradient
android:startColor="#1B2631"
android:endColor="#34495E "
android:angle="270" />
<stroke
android:width="1dp"
android:color="#00000000" />
<corners
android:radius="1dp" />
<padding
android:left="3dp"
android:top="3dp"
android:right="3dp"
android:bottom="3dp" />
</shape>
</item>
<item android:state_pressed="false">
<shape>
<gradient
android:startColor="#34495E"
android:endColor="#EBEDEF"
android:angle="270" />
<stroke
android:width="1dp"
android:color="#00000000" />
<corners
android:radius="1dp" />
<padding
android:left="3dp"
android:top="3dp"
android:right="3dp"
android:bottom="3dp" />
</shape>
</item>
</selector>
확인 지움 버튼
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape>
<gradient
android:startColor="#424949"
android:endColor="#F2F4F4"
android:angle="270" />
<stroke
android:width="1dp"
android:color="#00000000" />
<corners
android:radius="1dp" />
<padding
android:left="3dp"
android:top="3dp"
android:right="3dp"
android:bottom="3dp" />
</shape>
</item>
<item android:state_pressed="false">
<shape>
<gradient
android:startColor="#626567"
android:endColor="#F8F9F9"
android:angle="270" />
<stroke
android:width="1dp"
android:color="#00000000" />
<corners
android:radius="1dp" />
<padding
android:left="3dp"
android:top="3dp"
android:right="3dp"
android:bottom="3dp" />
</shape>
</item>
</selector>
사실 확인 지움 버튼은 숫자 버튼에서 그라데이션 색만 변경되었습니다.
그리고 layout xml은 다음과 같습니다.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_height="match_parent"
tools:context=".MainActivity">
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:cursorVisible="true" />
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/edit_text"
android:layout_above="@+id/viewFlipper"/>
<ViewFlipper
android:id="@+id/viewFlipper"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true">
<LinearLayout
android:id="@+id/firstViewFlipper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#c0c0c0"
android:orientation="vertical">
<TableLayout
android:id="@+id/tableLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="0,1,2">
<TableRow>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
</TableRow>
<TableRow>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
</TableRow>
<TableRow>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
</TableRow>
<TableRow>
<Button
android:id="@+id/key_ok"
android:layout_height="match_parent"
android:background="@drawable/key_spec_button_style"
android:textSize="20sp"
android:text="확 인" />
<Button
android:layout_height="100dp"
android:background="@drawable/key_button_style"
android:textSize="30sp"/>
<Button
android:id="@+id/key_cancel"
android:layout_height="match_parent"
android:background="@drawable/key_spec_button_style"
android:textSize="20sp"
android:text="지 움" />
</TableRow>
</TableLayout>
</LinearLayout>
</ViewFlipper>
</RelativeLayout>
EditText Textview ViewFlipper를 사용하였고, ViewFlipper 안에 Table을 너어서 숫자 패트를 만들었습니다.
ViewFlipper를 사용한 이유는 일반 ime 처럼 나타났다 사라졌다 할 수 있도록 하기 위해서 입니다.
다음은 android code 입니다.
package com.anapass.random_num_keyboard
import android.app.Activity
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Message
import android.util.DisplayMetrics
import android.view.KeyEvent
import android.view.View
import android.view.animation.*
import android.view.inputmethod.InputMethodManager
import android.widget.*
import java.lang.StringBuilder
import kotlin.random.Random
class MainActivity : AppCompatActivity() {
private lateinit var editText: EditText
private lateinit var flipper: ViewFlipper
private lateinit var tableLayout: TableLayout
private lateinit var textView : TextView
private val mActivity: Activity? = this
private var currentText : StringBuilder = StringBuilder()
private lateinit var imm: InputMethodManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val btnWidth: Int = getBtnWidth()
flipper = findViewById<ViewFlipper>(R.id.viewFlipper).apply {
visibility = View.VISIBLE
var inAni = AnimationUtils.loadAnimation(mActivity, R.anim.in_animation)
inAni.interpolator = AccelerateInterpolator()
setInAnimation(inAni)
var outAni = AnimationUtils.loadAnimation(mActivity, R.anim.out_animation)
outAni.interpolator = DecelerateInterpolator()
setOutAnimation(outAni)
}
tableLayout = findViewById<TableLayout>(R.id.tableLayout)
textView = findViewById<TextView>(R.id.text_view)
val emptyView = TextView(this)
flipper.addView(emptyView, 0)
editText = findViewById<EditText>(R.id.edit_text).apply {
setOnClickListener { v: View ->
imm.hideSoftInputFromWindow(editText.windowToken, 0)
if (flipper.currentView.id != R.id.firstViewFlipper) {
reOrderKeyboard(btnWidth)
flipper.visibility = View.VISIBLE
flipper.displayedChild = 1
}
}
}
imm.hideSoftInputFromWindow(editText.windowToken, 0)
editText.requestFocus()
findViewById<Button>(R.id.key_ok).apply {
setOnClickListener{ v : View ->
if (flipper.currentView.id == R.id.firstViewFlipper) {
flipper.displayedChild = 0
var showtxt = StringBuilder(textView.text)
showtxt.append("\n"+currentText)
textView.text = showtxt.toString()
currentText.clear()
editText.text.clear()
}
}
}
findViewById<Button>(R.id.key_cancel).apply {
setOnClickListener{ v : View ->
val curIndex = editText.selectionStart
var passwordStr = editText.text.toString()
var passWordLength = passwordStr.length
if (curIndex == 0 || passWordLength == 0) {
return@setOnClickListener
}
passwordStr.apply {
passwordStr = substring(0,curIndex-1)+substring(curIndex,passWordLength)
}
currentText.apply{
currentText = StringBuilder(toString().substring(0,curIndex-1)+toString().substring(curIndex,passWordLength))
}
passWordLength = passwordStr.length
editText.setText("")
for( i in 1 .. passWordLength){
editText.append("*")
}
editText.setSelection(curIndex-1);
}
}
reOrderKeyboard(btnWidth)
}
private fun reOrderKeyboard(btnWidth: Int) {
val keyNumberArr = ArrayList<String>()
for (i in 0..9) {
keyNumberArr.add(i.toString())
}
var tr: TableRow? = null
var btn: Button? = null
var randIndx: Int = 0;
var random = Random
for (i in 0..(tableLayout.childCount-1)) {
tr = tableLayout.getChildAt(i) as TableRow
for (i in 0..(tr.childCount-1)) {
btn = tr.getChildAt(i) as Button
if (btn.id == -1) {
randIndx = random.nextInt(keyNumberArr.size)
btn.text = keyNumberArr[randIndx]
btn.width = btnWidth/3
val keyTxt = btn.text
keyNumberArr.removeIf { x -> x == keyNumberArr[randIndx] }
btn.setOnClickListener { v: View ->
val curIndex = editText.selectionStart
var passwordStr = editText.text.toString()
val passWordLength = passwordStr.length
passwordStr.apply {
substring(0, curIndex) + keyTxt + substring(curIndex,passWordLength)
}
currentText.append(keyTxt)
editText.setText("")
for (i in 0 until curIndex) {
editText.append("*")
}
editText.append(keyTxt)
for (i in curIndex + 1 until passWordLength + 1) {
editText.append("*")
}
editText.setSelection(curIndex + 1)
mHandler.sendEmptyMessageDelayed(0,1000)
}
}
}
}
}
private var mHandler = Handler(){
msg: Message? ->
val curIndex = editText.selectionStart
editText.setText("")
for (i in 0 until curIndex) {
editText.append("*")
}
editText.setSelection(curIndex)
false
}
private fun getBtnWidth(): Int {
val displaymetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displaymetrics)
return displaymetrics.widthPixels
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if(keyCode == KeyEvent.KEYCODE_BACK){
if(flipper.currentView.id == R.id.firstViewFlipper){
flipper.setDisplayedChild(0)
}else{
return super.onKeyDown(keyCode, event)
}
}
return true
}
}
보시면 code는 그리 길지 않습니다.
숫자 view를 누르면 edittext에 값이 추가 되고 edittext에는 *로 보이지만 따로 입력한 숫자를 저장하여 최종적으로 확인을 눌렸을 경우 그 값을 사용합니다. 그리고 keydown시 back 키가 들어올경우 custom keyboard가 올라와 있으면 custom keyboard를 내리고, 확인 버튼을 눌렸을 때는 Textview에 입력한 숫자를 노출하면 custom keyboard를 내립니다. 지 움 버튼을 누르면 입력한 값이 지워 집니다.
이때 중간 중간에 InputMethodManager를 사용하여 softkeyboard를 hide하고 있는데, 이러지 않으면 custom keyboard위로 ime가 나타나기 떄문입니다. (지금 만든건 keyboard 같지만 사실 view를 사용여 만든 UI일 뿐입니다. ime가 아닙니다.)
그리고 animation을 사용하여 custom keyboard가 나타나고 사라지는 animaiton을 추가하였습니다.
animation은 res 밑에 anim라는 Diratory를 만들고 그 안에
나타나는 animation은
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0%p"
android:toXDelta="0%p"
android:fromYDelta="100%p"
android:toYDelta="0%p"
android:duration="500"
/>
</set>
사라지는 animation은
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="0%p"
android:toXDelta="0%p"
android:fromYDelta="0%p"
android:toYDelta="100%p"
android:duration="500"
/>
</set>
사용하시면 됩니다.
그렇게 완성된 app 화면은 다음과 같습니다.
'2023년 이전 > Android' 카테고리의 다른 글
Android - WorkManager(1) (1) | 2019.10.16 |
---|---|
Android -LiveData (0) | 2019.10.15 |
Android - ViewModel (0) | 2019.10.15 |
Android - Kotlin을 사용하여 Listener 등록 (0) | 2019.08.29 |
Android 잠금화면 위에 Activity 보여주기 (0) | 2019.08.28 |