링크는 프로덕션 버전이 출시 완료되면 올리겠습니다.

일단은 스크린샷부터 :D

내일쯤이면 검토가 끝나지 않을까 싶은데~

두근두근하네요^^

 

'개발 > Android' 카테고리의 다른 글

[Kotlin/코드] MVP (2)  (0) 2022.02.14
[Kotlin/코드] MVP 패턴  (1) 2022.02.14
[프로젝트] 8일차 - 캡처 & FileProvider & 공유  (0) 2021.09.27
[프로젝트] 7일차 - Room ( Database )  (0) 2021.09.25
[프로젝트] 6일차 - atan2  (0) 2021.09.25

1. 화면 캡처

원래는 view.getDrawingCache()를 사용했으나 이게 deprecated 되어서 다른 방법으로 캡처 기능을 구현하였다.

 

https://newbedev.com/view-getdrawingcache-is-deprecated-in-android-api-28

 

Programming tutorials | Newbedev

Checkout new tutorials and guides for programming languages Javascript, Python, Java, Golang, C++, PHP

newbedev.com

아래는 뷰를 캡쳐해서 저장된 파일의 경로를 가져오는 capture 메소드

fun capture(): String? {
    val now = SimpleDateFormat("yyyyMMdd_hhmmss").format(Date(System.currentTimeMillis()))
    val mPath = cacheDir.absolutePath+"/$now.jpg"

    var bitmap: Bitmap? = null
    val captureView = window.decorView.rootView	//캡처할 뷰

    bitmap = Bitmap.createBitmap(captureView.width, captureView.height, Bitmap.Config.ARGB_8888)

    var canvas = Canvas(bitmap)	
    captureView.draw(canvas)	

    if(bitmap == null) {
        return null
    }else {
        val imageFile = File(mPath)
        val outputStream = FileOutputStream(imageFile)
        outputStream.use {
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
            outputStream.flush()
        }
    }
    return mPath
}

 

2. FileProvider

https://keykat7.blogspot.com/2021/02/android-fileprovider.html

나는 캐시 디렉토리 안에 screenshots라는 폴더 안에 스크린샷을 저장하려고 했다.

그런데 파일에서 Uri를 얻기 위해서 FileProvider를 이용하여야 한다.

 

① resource에 fileprovider.xml 추가

<paths>
    <cache-path
        name="screenshots"
        path="." />
</paths>

② Manifest.xml에 코드 추가 

<application
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.test.packagename.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/fileprovider" />
    </provider>
    ...
/>

③ 이미지 공유하는 코드 추가

if(captureFile == null) {
    //캡쳐 실패한 경우
}else{
    try {
        val intent = Intent(Intent.ACTION_SEND)
        val fileUri : Uri? = FileProvider.getUriForFile(getContext(), "$packageName.fileprovider", captureFile!!)
        fileUri?.let {
            intent.putExtra(Intent.EXTRA_STREAM, fileUri)
            intent.type = "image/*"
            intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
            startActivity(intent)
        }
    }catch(e: ActivityNotFoundException) {
        //공유할 수 있는 어플리케이션이 없을 때 처리
    }
}

 

 

리스트를 내부 DB에 저장하고 불러오게 하고 싶었다.

라떼는 SQLite에 직접 접근해서.. (이하 생략)

요즘은 세상이 좋아져서 Room이라는 걸로 대신해서 쓸 수 있는 모양이다.

 

Room을 이해하기 위해서는 RoomDatabase, Dao, Entity 이렇게 세 가지를 이해해야하는데

이것은 다른 사람의 블로그나 공식 문서를 보면 아주 잘 설명이 되어 있으니

굳이 내가 적지는 않겠다.

 

다만, 나의 경우 데이터 모델이

val idx: Int
var title: String
var items: Array<String>

처럼 Array를 포함하고 있었는데, 이 경우에는 DB에 Array를 JSON 타입으로 저장하여야 했다.

그리고 이를 위해 @TypeConverter, @TypeConverters 어노테이션을 이용하여야 했다.

 

이는 아래 블로그를 참고하기 바란다.

https://jinsangjin.tistory.com/56

 

Room에서 List 사용하기 #Kotlin #TypeConverter #Android

Room에서 다음과 같이 선언하면 그냥 쓸 수 있을 줄 알았다. @ColumnInfo(name = "word_list") var wordList: List ?= null 하지만 다음과 같이 선언하고 build를 시작하면 error가 뜬다. 에러내용은 "typerConver..

jinsangjin.tistory.com

 

마지막으로 Room을 통한 DB 접속은 메인 스레드에서 실행될 수 없다.

따라서 코루틴을 사용하여 접근하였는데

 

CoroutineScope(Dispatchers.Main).launch {
    var dataList: ArrayList<SaveDTO>? = null
    CoroutineScope(Dispatchers.Default).async {
        val db = AppDatabase.getInstance(context)
        //DB에서 하는 일들
    }.await()
	//메인 스레드에서 하는 일들
}

이런 식으로 사용하였다.

 


이제 기본 기능은 얼추 완성된 것 같다.

남은 건 스샷찍어서 공유하는 것 뿐이려나..

Math.atan2를 사용하면 두 점 사이의 라디안 각도를 얻을 수 있다.

 

상대적인 위치이기 때문에 

 

Math.atan2(나중점.x - 먼젓번점.x, 나중점.y - 먼젓번점.y)를 해서 사용한 뒤

 

나온 값에 180을 곱하고 Math.PI로 나누어주면 우리가 아는 각도가 나온다.

 

나는 이걸 이용해서 설정모드일 때는 값을 설정하도록,

설정모드가 아닐 때는 터치한 곳의 값을 표시하도록 하였다.

 

layout xml 파일에서 배경과 배경 패딩, 그리고 리스트를 주기 위해서 커스텀 attibute를 설정하였다.

 

1. res/values/attrs.xml

<resources>
    <declare-styleable name="RouletteView">
        <attr name="rouletteBackground" format="reference"></attr>
        <attr name="roulettePadding" format="dimension"></attr>
        <attr name="listItem" format="reference"></attr>
    </declare-styleable>
</resources>

rouletteBackground - 룰렛 배경으로 넣는 이미지

reference 포맷으로 하면 "@drawable/XXX" 이런식으로 입력해야 한다.

 

roulettePadding - 룰렛 배경에서 얼마만큼 패딩을 줄지

Xdp로 받기 위해 dimension 포맷으로 설정하였다.

 

listItem - 룰렛 아이템을 array resource로 관리할 수 있게 설정하였다.

 

2. RouletteView.kt

constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
    initialize(context, attrs!!)
}

constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
    context,
    attrs,
    defStyleAttr
) {
    initialize(context!!, attrs!!)
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
constructor(
    context: Context?,
    attrs: AttributeSet?,
    defStyleAttr: Int,
    defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
    initialize(context!!, attrs!!)
}


private fun initialize(context: Context, attrs: AttributeSet) {
        var attrList: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.RouletteView)
        var mBackground = attrList.getDrawable(R.styleable.RouletteView_rouletteBackground)
        background = mBackground
        backgroundPadding = attrList.getDimensionPixelSize(R.styleable.RouletteView_roulettePadding, 0)
        stringArr = attrList.getTextArray(R.styleable.RouletteView_listItem)
        attrList.recycle()

        arcPaintArr = Array(stringArr.size) { Paint()}
        textPaintArr = Array(stringArr.size) { Paint()}
        for (i in stringArr.indices) {
            var index:Int = (random()*_colorArr.size).toInt()
            arcPaintArr[i].color = _colorArr[index].toInt()
            textPaintArr[i].apply {
                color = Color.RED
                textSize = 24f
                textAlign = Paint.Align.CENTER
            }
        }
    }

attr로 넘어온 속성들을 처리해주도록 하였고

원래 init 스코프? 에서 처리하던 Paint 객체 생성 작업의 위치를 옮겼다.

색상은 우선 리스트에서 랜덤으로 선택해서 나오도록 하였음..

 

3. activity_main.xml

이런식으로 설정해주면..

 

이런식으로 나오게 된다 ㅎㅎ

 

 

 

슬슬 그럴듯한 모습이 나오기 시작했다.

 

다음은 사용자한테서 리스트를 입력받는 팝업과 

입력받아서 룰렛 세팅하는 것까지 해야지 :)

+ Recent posts