'안드로이드/Kotlin 기능'에 해당되는 글 25건

728x90

Java 기반 ViewPager 와 동일한 Layout 으로 테스트하고 적어둔다.


앱 build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.link2me.android.viewpger"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    //implementation "org.jetbrains.anko:anko:$anko_version"

    implementation 'com.google.android.material:material:1.1.0'
    //implementation 'androidx.cardview:cardview:1.0.0'  // 레이아웃으로 사용할 CardView
    //implementation 'androidx.recyclerview:recyclerview:1.1.0'

    // 이미지 출력용 Glide
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    kapt 'com.github.bumptech.glide:compiler:4.11.0' // Kotlin plugin doesn't pick up annotationProcessor dependencies

}



전체적인 코드는 아래 그림을 참조하면 알 수 있다.

MainActivity.kt, Fragment1.kt, Fragment2.kt, Fragment3.kt, ViewPagerAdapter.kt


먼저 activity_main.xml 을 선택하고 2번 검색버튼을 눌러서 tabLayout을 검색하면 Material Design 기반 tabLayout이 선택된다. Material Design 기반으로 하는 이유는 화면이 깔끔하기 때문이다.



Layout 코드는

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="1dp"
        app:tabTextColor="@color/colorDarkGray"
        app:tabSelectedTextColor="@color/colorWhite"
        app:layout_collapseMode="pin"
        app:tabGravity="fill"
        app:tabBackground="@drawable/tab_color_selector"
        app:layout_constraintBottom_toTopOf="@+id/viewPager"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Monday" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Tuesday" />

        <com.google.android.material.tabs.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Wednesday" />
    </com.google.android.material.tabs.TabLayout>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="1dp"
        android:layout_marginEnd="1dp"
        android:layout_marginBottom="1dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tabLayout" />

</androidx.constraintlayout.widget.ConstraintLayout>


코틀린 코드

먼저 각각의 페이지를 담당하는 Fragment 를 만든다.

그런 다음 ViewPager 와 연결해준다.


class Fragment1 : Fragment() {

    // https://blog.mindorks.com/android-material-tabs-with-kotlin 참조해서 작성했다.

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_1, container, false)
        return view
    }
}

class Fragment2 : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_2, container, false)
        return view
    }
}

class Fragment3 : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_3, container, false)
        return view
    }
}


ViewPagerAdapter 코드 구현

class ViewPagerAdapter(manager: FragmentManager) : FragmentPagerAdapter(manager) {
    private val mFragmentList = ArrayList<Fragment>()
    private val mFragmentTitleList = ArrayList<String>()

    override fun getItem(position: Int): Fragment {
        return mFragmentList.get(position)
    }

    override fun getCount(): Int {
        return mFragmentList.size
    }

    override fun getPageTitle(position: Int): CharSequence? {
        return mFragmentTitleList[position]
    }

    fun addFragment(fragment: Fragment, title: String) {
        mFragmentList.add(fragment)
        mFragmentTitleList.add(title)
    }
}


MainActivity.kt 코드

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val adapter = ViewPagerAdapter(supportFragmentManager)
        adapter.addFragment(Fragment1(), "사랑")
        adapter.addFragment(Fragment2(), "열정")
        adapter.addFragment(Fragment3(), "노력")
        viewPager.adapter = adapter
        tabLayout.setupWithViewPager(viewPager)
    }
}


테스트에 사용한 코드를 첨부

viewpger.zip

여기까지는 기본적인 ViewPager 동작에 대한 이해라고 보면 된다.


ViewPager 를 이용한 전자액자 예제는 https://github.com/junsuk5/kotlin-android 오준석의 생존코딩 코틀린편 자료 9장을 받아서 테스트해보면 된다. 최신으로 업데이트되어 있다.


Activity에서 Fragment 로 데이터를 보내려면 어떻게 해야 할까?

전자액자 예제를 기반으로 데이터 전달하는 걸 참조하는 예시라고 보면 된다.

class MainActivity : AppCompatActivity() {

    private lateinit var code: String

    lateinit var mWebViewFragment: Fragment1

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        code = "1462"
        Log.e("MainActivity code",code)
        mWebViewFragment = Fragment1.newInstance(code)

        val adapter = ViewPagerAdapter(supportFragmentManager)
        adapter.addFragment(mWebViewFragment, "웹뷰")
        adapter.addFragment(Fragment2(), "열정")
        adapter.addFragment(Fragment3(), "노력")
        viewPager.adapter = adapter
        tabLayout.setupWithViewPager(viewPager)
    }
}

class Fragment1 : Fragment() {

    private lateinit var mWebView: WebView

    var code: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            code = it.getString(ARG_URI)
            Log.e("WebviewFragment code",code)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        var view = inflater.inflate(R.layout.fragment_webview, container, false).apply {
            mWebView = findViewById(R.id.webView)
        }
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val url: String = "http://www.abc.com/getData.php?code=${code}"
        Log.e("onViewCreated code",url)
        url?.let { MyWebView(it) }
    }

    fun MyWebView(url: String){
        mWebView.apply {
            settings.javaScriptEnabled = true
            settings.setSupportZoom(true)
            settings.builtInZoomControls = true
            settings.displayZoomControls = false
            settings.loadsImagesAutomatically = true
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                settings.safeBrowsingEnabled = true  // api 26
            }
            settings.domStorageEnabled = true
            mWebView.webViewClient = WebViewClient()
        }
        mWebView.loadUrl(url)
    }

    companion object { // (1) 제일 먼저 호출됨
        // const키워드를 써서 전역변수 만들기
        private const val ARG_URI = "uri"

        @JvmStatic
        fun newInstance(uri: String) =
            Fragment1().apply {
                arguments = Bundle().apply {
                    putString(ARG_URI, uri)

                }
            }
    }

}




'안드로이드 > Kotlin 기능' 카테고리의 다른 글

[코틀린] Tedpermission 사용방법  (0) 2020.05.06
[코틀린] PrefsHelper  (0) 2020.05.04
[코틀린] webView 예제1  (0) 2020.04.20
[코틀린] RecyclerView Part 1  (0) 2020.04.19
Checking if a URL Exists in Kotlin  (0) 2020.04.17
블로그 이미지

Link2Me

,
728x90

코틀린에서 WebView 기능을 구글링해서 코드를 여러가지 추가하면서 테스트하고 적어둔다.

책에 나온 코드는 매우 간단하다.

사실 코드가 간단해도 동작하는데 전혀 문제는 없다.


Project build.gradle

buildscript {
    ext.kotlin_version = '1.3.72'
    ext.anko_version='0.10.8'

    repositories {
        google()
        jcenter()
       
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.6.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}


앱 build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.link2me.android.webview"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    implementation "org.jetbrains.anko:anko:$anko_version"

    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.cardview:cardview:1.0.0'  // 레이아웃으로 사용할 CardView
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}



import android.app.Activity
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
import android.view.inputmethod.EditorInfo
import android.webkit.*
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private lateinit var mWebView: WebView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mWebView = findViewById(R.id.webView)
        val url: String = "http://link2me.tistory.com/"
        MyWebView(url)

        urlEditText.setOnEditorActionListener{_, actionId, _->
            if(actionId == EditorInfo.IME_ACTION_SEARCH){
                var url: String = urlEditText.text.toString()
                if(!(url.contains("http://") || url.contains("https://"))){
                    url = "http://${url}"
                }
                MyWebView(url)
                true
            } else {
                false
            }
        }

    }

    fun MyWebView(url: String){
        mWebView.apply {
            settings.javaScriptEnabled = true
            // Enable and setup web view cache
            settings.setAppCacheEnabled(true)
            settings.cacheMode = WebSettings.LOAD_DEFAULT
            settings.setAppCachePath(cacheDir.path)
            // Enable zooming in web view
            settings.setSupportZoom(true)
            settings.builtInZoomControls = true
            settings.displayZoomControls = true
            // Enable disable images in web view
            settings.blockNetworkImage = false
            // Whether the WebView should load image resources
            settings.loadsImagesAutomatically = true

            settings.domStorageEnabled = true
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                settings.safeBrowsingEnabled = true  // api 26
            }
            //settings.pluginState = WebSettings.PluginState.ON
            settings.useWideViewPort = true
            settings.loadWithOverviewMode = true
            settings.javaScriptCanOpenWindowsAutomatically = true
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                settings.mediaPlaybackRequiresUserGesture = false
            }

            // More optional settings, you can enable it by yourself
            settings.domStorageEnabled = true
            settings.setSupportMultipleWindows(true)
            settings.loadWithOverviewMode = true
            settings.allowContentAccess = true
            settings.setGeolocationEnabled(true)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                settings.allowUniversalAccessFromFileURLs = true
            }

            settings.allowFileAccess = true

            // WebView settings
            mWebView.fitsSystemWindows = true

            mWebView.webViewClient = MyWebViewClient(this@MainActivity)
        }
        mWebView.loadUrl(url)
    }

    class MyWebViewClient internal constructor(private val activity: Activity) : WebViewClient() {

        @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
        override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
            val url: String = request?.url.toString();
            view?.loadUrl(url)
            return true
        }

        override fun shouldOverrideUrlLoading(webView: WebView, url: String): Boolean {
            webView.loadUrl(url)
            return true
        }

        override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
            Toast.makeText(activity, "Got Error! $error", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
            mWebView.goBack()
            return true
        }
        return super.onKeyDown(keyCode, event)
    }

    override fun onBackPressed() {
        if(mWebView.canGoBack()){
            mWebView.goBack() // 이전 페이지로 갈 수 있다면 이동하고
        } else {
            super.onBackPressed() // 더 이상 이전 페이지가 없을 때 앱이 종료된다.
        }
    }
}


XML Layout 만드는 방법은 별도 적지 않고 테스트한 소스코드를 첨부한다.

WebView.zip


'안드로이드 > Kotlin 기능' 카테고리의 다른 글

[코틀린] PrefsHelper  (0) 2020.05.04
[코틀린] ViewPager 만들기  (0) 2020.04.24
[코틀린] RecyclerView Part 1  (0) 2020.04.19
Checking if a URL Exists in Kotlin  (0) 2020.04.17
Splash Screen with Kotlin  (0) 2020.04.09
블로그 이미지

Link2Me

,
728x90
앱 build.gradle Implementation 추가
RecyclerView는 기본 API에 제공되어 있지 않기 때문에, Support Library 추가를 해야 사용할 수 있다.

추가 방법의 한가지이다.

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'


android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.link2me.android.recyclerview"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }
}


dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.cardview:cardview:1.0.0'  // 레이아웃으로 사용할 CardView
    implementation 'androidx.recyclerview:recyclerview:1.1.0'

    // 이미지 출력용 Glide
    implementation 'com.github.bumptech.glide:glide:4.11.0'
    annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

    kapt 'com.github.bumptech.glide:compiler:4.11.0'
    // Kotlin plugin doesn't pick up annotationProcessor dependencies

}
 


데이터 클래스 정의

data class PersonModel (
    var idx: String,
    var name: String,
    var position : String,
    var mobileNO: String,
    var checkBoxState: Boolean
)


Layout 만들기

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:listitem="@layout/item_listview" />

</androidx.constraintlayout.widget.ConstraintLayout>
 


Item View 생성

cardView 만드는 방법은 https://link2me.tistory.com/1813 참조


Adapter 구현

import android.content.Context
import android.os.AsyncTask
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.link2me.android.recyclerview.model.PersonModel
import kotlinx.android.synthetic.main.item_listview.view.*
import java.net.HttpURLConnection
import java.net.URL

class PersonAdapter(val list: List<PersonModel>, val context: Context) : RecyclerView.Adapter<PersonAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        // XML객체를 실제 View 로 만들어주기 위한 작업
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_listview,parent,false)
        return ViewHolder(view)
    }

    override fun getItemCount(): Int {
        return list.count()
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        // 생성된 ViewHolder가 화면에 표시될 때 실제 데이터를 바인딩 해주는 함수이다.
        // ?. 연산자를 사용하면 null 값이 아닌 경우에만 호출된다.
        //holder?.bindItems(list[position], context) // 이렇게 하거나 아래줄과 같이 사용
        (holder as ViewHolder).bindItems(list[position],context)
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        // 코틀린에서는 기본적으로 null 값을 허용하지 않는다.

        // null 값을 허용하려면 자료형의 오른쪽에 ? 기호를 붙여준다.
        // 변수 뒤에 !!를 추가하면 null 값이 아님을 보증하게 된다.
        // ?. 연산자를 사용하면 null 값이 아닌 경우에만 호출된다.
        val mImage = itemView?.findViewById(R.id.listitem_image) as ImageView

        fun bindItems (person: PersonModel, context: Context) {
            val imageUri: String = "http://www.abc.com/photos/${person.idx}.jpg"
            if (mImage != null) {
                val urlexist: Boolean = URLExistTask().execute(imageUri).get()
                if (urlexist) {
                    Glide.with(context).load(imageUri).override(126, 160).into(mImage)
                } else {
                    val resourceId: Int = R.drawable.photo_base
                    Glide.with(context).load(resourceId).override(126, 160).into(mImage)
                }
            };
            itemView.listitem_title.text = person.name
            itemView.listitem_subtext1.text = person.position
            itemView.listitem_subtext2.text = person.mobileNO
            itemView.listitem_checkbox.isChecked = person.checkBoxState

            itemView.setOnClickListener({
                Toast.makeText(context,person.name,Toast.LENGTH_SHORT).show()
            })

        }

    }

    inner class URLExistTask : AsyncTask<String, Void, Boolean>() {
        override fun doInBackground(vararg params: String?): Boolean {
            return try {
                HttpURLConnection.setFollowRedirects(false)
                val con: HttpURLConnection = URL(params[0]).openConnection() as HttpURLConnection
                con.setConnectTimeout(1000)
                con.setReadTimeout(1000)
                con.setRequestMethod("HEAD")
                con.getResponseCode() === HttpURLConnection.HTTP_OK
            } catch (e: Exception) {
                e.printStackTrace()
                false
            }
        }
    }

}

서버에 실제 이미지가 존재하는지 여부를 확인하기 위한 URLExistTask 메소드를 구현한다.


Adapter 생성

class MainActivity : AppCompatActivity() {

    var adapter: PersonAdapter?= null
    var personList: List<PersonModel> = ArrayList()
    // Java 에서는 new 키워드로 객체를 생성하지만, 코틀린에서는 new 키워드를 사용하지 않는다.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        personList = listOf(
            PersonModel("250","홍길동","차장","010-111-1000",true),
            PersonModel("340","강감찬","부장","010-111-1001",true),
            PersonModel("419","이순신","본부장","010-111-1002",true),
            PersonModel("490","김구","부장","010-111-1003",true),
            PersonModel("503","양만춘","대리","010-111-1004",true),
            PersonModel("3","김두환","과장","010-111-1005",true),
            PersonModel("1","정성룡","사원","010-111-1006",true)
        )

        adapter = PersonAdapter(personList,this)
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.setHasFixedSize(true)
        recyclerView.adapter = adapter
    }
}
 


리스트는 서버에서 가져온 리스트라고 가정하고 Local 데이터를 생성하여 테스트한다.


테스트에 사용한 소스코드를 첨부한다.

recyclerview_kotlin.zip


참고하면 좋은 자료

https://www.andreasjakl.com/kotlin-recyclerview-for-high-performance-lists-in-android/


Adapter 구현 수정사항

class PersonAdapter(val items: List<PersonModel>, val context: Context) : RecyclerView.Adapter<PersonAdapter.ViewHolder>() {

    override fun getItemCount(): Int = items.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        // 생성된 ViewHolder가 화면에 표시될 때 실제 데이터를 바인딩 해주는 함수이다.
        //(holder as ViewHolder).bindItems(list[position],context)
        var item = items[position]
        val listener = View.OnClickListener {it ->
            Toast.makeText(it.context, "Clicked: ${item.name}", Toast.LENGTH_SHORT).show()
        }
        holder.apply {
            // apply()함수는 블록에 객체 자신이 리시버 객체로 전달되고 이 객체가 반환된다.
            // 객체의 상태를 변화시키고 그 객체를 다시 반환할 때 주로 사용한다.
            bindItems(listener, item, context)
            itemView.tag = item
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        // XML객체를 실제 View 로 만들어주기 위한 작업
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_listview,parent,false)
        return ViewHolder(view)
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        // 코틀린에서는 기본적으로 null 값을 허용하지 않는다.

        // null 값을 허용하려면 자료형의 오른쪽에 ? 기호를 붙여준다.
        // 변수 뒤에 !!를 추가하면 null 값이 아님을 보증하게 된다.
        // ?. 연산자를 사용하면 null 값이 아닌 경우에만 호출된다.
        val mImage = itemView?.findViewById(R.id.listitem_image) as ImageView

        fun bindItems (listener: View.OnClickListener,person: PersonModel, context: Context) {
            val imageUri: String = "http://www.abc.com/photos/${person.idx}.jpg"
            if (mImage != null) {
                val urlexist: Boolean = URLExistTask().execute(imageUri).get()
                if (urlexist) {
                    Glide.with(context).load(imageUri).override(126, 160).into(mImage)
                } else {
                    val resourceId: Int = R.drawable.photo_base
                    Glide.with(context).load(resourceId).override(126, 160).into(mImage)
                }
            };
            itemView.listitem_title.text = person.name
            itemView.listitem_subtext1.text = person.position
            itemView.listitem_subtext2.text = person.mobileNO
            itemView.listitem_checkbox.isChecked = person.checkBoxState
            itemView.setOnClickListener(listener)
        }

    }

    inner class URLExistTask : AsyncTask<String, Void, Boolean>() {
        override fun doInBackground(vararg params: String?): Boolean {
            return try {
                HttpURLConnection.setFollowRedirects(false)
                val con: HttpURLConnection = URL(params[0]).openConnection() as HttpURLConnection
                con.setConnectTimeout(1000)
                con.setReadTimeout(1000)
                con.setRequestMethod("HEAD")
                con.getResponseCode() === HttpURLConnection.HTTP_OK
            } catch (e: Exception) {
                e.printStackTrace()
                false
            }
        }
    }

}
 


'안드로이드 > Kotlin 기능' 카테고리의 다른 글

[코틀린] PrefsHelper  (0) 2020.05.04
[코틀린] ViewPager 만들기  (0) 2020.04.24
[코틀린] webView 예제1  (0) 2020.04.20
Checking if a URL Exists in Kotlin  (0) 2020.04.17
Splash Screen with Kotlin  (0) 2020.04.09
블로그 이미지

Link2Me

,
728x90

서버에서 사진 정보를 가져오는데 사진 파일이 존재하는지 URL 로 확인하는 코드이다.

RecyclerView 에 사진을 보여주는 cardView Item 작성할 때 적용했다.

메인쓰레드에서 처리하면 에러가 발생하므로 AsyncTask 를 이용한다.



앱 build.gradle

// 이미지 출력용 Glide
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'

    inner class URLExistTask : AsyncTask<String, Void, Boolean>() {
        override fun doInBackground(vararg params: String?): Boolean {
            return try {
                HttpURLConnection.setFollowRedirects(false)
                val con: HttpURLConnection = URL(params[0]).openConnection() as HttpURLConnection
                con.setConnectTimeout(1000)
                con.setReadTimeout(1000)
                con.setRequestMethod("HEAD")
                con.getResponseCode() === HttpURLConnection.HTTP_OK
            } catch (e: Exception) {
                e.printStackTrace()
                false
            }
        }
    }
 


사용법

val mImage: ImageView? = itemView?.findViewById(R.id.listitem_image)

val imageUri: String = "http://www.abc.com/photos/${person.idx}.jpg"

if (mImage != null) {
    val urlexist: Boolean = URLExistTask().execute(imageUri).get()
    if (urlexist) {
        Glide.with(context).load(imageUri).override(126, 160).into(mImage)
    } else {
        val resourceId: Int = R.drawable.photo_base
        Glide.with(context).load(resourceId).override(126, 160).into(mImage)
    }
};


#### 함수를 만드는 과정 ####

먼저 Java 로 된 함수를 찾는다.

public static boolean exists(String URLName) {
    try {
        HttpURLConnection.setFollowRedirects(false);
        HttpURLConnection con = (HttpURLConnection) new URL(URLName).openConnection();
        con.setConnectTimeout(1000);
        con.setReadTimeout(1000);
        con.setRequestMethod("HEAD");
        return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
}


그런데 메인쓰레드에서 처리하면 android.os.NetworkOnMainThreadException kotlin 에러가 발생하므로 AsyncTask 에서 동작하도록 코드를 수정해야 한다.

'안드로이드 > Kotlin 기능' 카테고리의 다른 글

[코틀린] PrefsHelper  (0) 2020.05.04
[코틀린] ViewPager 만들기  (0) 2020.04.24
[코틀린] webView 예제1  (0) 2020.04.20
[코틀린] RecyclerView Part 1  (0) 2020.04.19
Splash Screen with Kotlin  (0) 2020.04.09
블로그 이미지

Link2Me

,
728x90

온라인 코틀린 강좌 들으면서 디자인을 고려하여 Material Design 구글 검색 참조 및 코드 보완 테스트 하고 있다.

구글 검색어 : android kotlin splash

검색어로 찾을 결과 https://levelup.gitconnected.com/a-tutorial-on-building-a-splash-screen-with-kotlin-in-android-studio-dc647cd52f9b 를 참조했고, UI 관련 파일 내용은 약간 다르다.


앱 build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.link2me.android.splash"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility = 1.8
        targetCompatibility = 1.8
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'com.google.android.material:material:1.0.0'
}
 


AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.link2me.android.splash">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SplashActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity" android:theme="@style/AppTheme.DayNight"/>

    </application>

</manifest>
 


color.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#00A99D</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorPrimaryDarker">#CC1D1D</color>
    <color name="colorAccent">#D81B60</color>

    <color name="colorBlack">#ff000000</color>
    <color name="colorWhite">#ffffffff</color>

    <color name="colorGray">#ff8a8a8a</color>
    <color name="colorDarkGray">#4c4c4e</color>
    <color name="colorLightGray">#ffeaeaea</color>

    <color name="colorMint">#ff00c0aa</color>
    <color name="colorLightMint">#1000c0aa</color>

    <color name="colorJet">#222222</color>
    <color name="colorOil">#333333</color>
    <color name="colorMonsoon">#777777</color>
    <color name="colorJumbo">#888888</color>
    <color name="colorAluminum">#999999</color>
    <color name="colorBase">#AAAAAA</color>
    <color name="colorIron">#CCCCCC</color>
</resources>
 


styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <!-- This style is for the splash screen -->
    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="AppTheme.DayNight" parent="Theme.AppCompat.DayNight.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>

        <item name="colorControlNormal">@color/colorIron</item>
        <item name="colorControlActivated">@color/colorWhite</item>
        <item name="colorControlHighlight">@color/colorWhite</item>
        <item name="android:textColorHint">@color/colorIron</item>

        <item name="colorButtonNormal">@color/colorPrimaryDarker</item>
        <item name="android:colorButtonNormal">@color/colorPrimaryDarker</item>
    </style>

    <style name="AppTheme.Dark.Dialog" parent="Theme.AppCompat.Dialog">
        <item name="colorAccent">@color/colorWhite</item>
        <item name="android:textColorPrimary">@color/colorIron</item>
        <item name="android:background">@color/colorPrimary</item>
    </style>
</resources>
 


activity_splash.xml

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:layout_height="match_parent"
    tools:context=".SplashActivity">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/bg_splash"/>

</androidx.constraintlayout.widget.ConstraintLayout>
 


SplahsAcitivity.kt

import android.content.Intent
import android.os.Bundle
import android.os.Handler
import androidx.appcompat.app.AppCompatActivity

class SplashActivity : AppCompatActivity() {
    private val SPLASH_TIME_OUT:Long = 2000 // 2 sec

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_splash)

        Handler().postDelayed({
            startActivity(Intent(this,MainActivity::class.java))
            finish()  // close this activity
        }, SPLASH_TIME_OUT)
    }
}
 


위 예제코드 파일

slpah_src.zip



'안드로이드 > Kotlin 기능' 카테고리의 다른 글

[코틀린] PrefsHelper  (0) 2020.05.04
[코틀린] ViewPager 만들기  (0) 2020.04.24
[코틀린] webView 예제1  (0) 2020.04.20
[코틀린] RecyclerView Part 1  (0) 2020.04.19
Checking if a URL Exists in Kotlin  (0) 2020.04.17
블로그 이미지

Link2Me

,