github.com/itggangpae/KotlinAndroid/kotlinProject : 코틀린
github.com/itggangpae/KotlinAndroid/KotlinAndroid : 안드로이드
itggangpae/KotlinAndroid
Contribute to itggangpae/KotlinAndroid development by creating an account on GitHub.
github.com
File -> New -> Project from Version Control 에서 다운로드 받을 URL 입력
ListView 에서 셀을 클릭하면 link 를 하위 Activity 로 넘겨서 WebView 에 link 를 출력하기
1. Empty Activity 를 추가 - LinkActivity
2. LinkActivity의 화면 디자인 - activity_link.xml 파일
=> 화면에 가득차게 배치
3. intent 를 통해 화면이 넘어가는 Activity 구현
// Listview 의 셀 클릭 이벤트 처리
// Parent 는 이벤트가 발생한 부모 뷰 - listview
// view 는 이벤트가 발생한 항목 뷰 - 셀을 조작하고자 할 때 이용
// position 은 셀의 인덱스 - 데이터를 찾아 올 때
// id 는 셀을 만들 때 넘겨준 id
listview.onItemClickListener = AdapterView.OnItemClickListener{
parent, view, position, id ->
// 데이터 찾아오기
val movie = movieList!!.get(position)
// 넘겨줄 데이터는 link
val link : String = movie.link!!
// 호출할 Activity 를 Intent 로 생성
val intent:Intent = Intent(this, LinkActivity::class.java)
intent.putExtra("link", link)
startActivity(intent)
}
4. LinkActivity.kt 파일의 onCreate 메소드의 link 를 받아서 WebView 에 출력하는 코드 추가
// 데이터 찾아오기
val intent = getIntent()
val link = intent.getStringExtra("link")
// webview 에 link 를 출력
webview.webViewClient = WebViewClient()
var settings = webview.settings
settings!!.javaScriptEnabled = true
webview.loadUrl(link!!)
LinkActivity.kt
package com.example.adapterview
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
import android.widget.AbsListView
import android.widget.AdapterView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_movie_list.*
import org.json.JSONObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class MovieListActivity : AppCompatActivity() {
//다운로드 받은 문자열을 저장하기 위한 프로퍼티
var json:String? = null
//다운로드 스레드를 위한 프로퍼티
var th : MovieThread? = null
//데이터 목록을 저장할 리스트
var movieList : MutableList<Movie>? = null
//데이터 개수를 저장할 변수
var count:Int? = null
//ListView에 출력하기 위한 Adapter
//var movieAdapter: ArrayAdapter<Movie>? = null
var movieAdapter: MovieAdapter? = null
// 현재 페이지 번호를 저장할 프로퍼티
var pageno = 1
// 마지막 페이지 여부를 저장할 플래그 프로퍼티
var lastItemVisibleFlag = false
inner class MovieThread : Thread() {
override fun run() {
try {
//다운로드 받을 주소 생성
var url: URL = URL("http://cyberadam.cafe24.com/movie/list?page=${pageno}")
//연결 객체 생성
val con =
url!!.openConnection() as HttpURLConnection
//옵션 설정
con.requestMethod = "GET" //전송 방식 선택
con.useCaches = false //캐시 사용 여부 설정
con.connectTimeout = 30000 //접속 시도 시간 설정
con.readTimeout = 3000 //읽는데 걸리는 시간 설정
con.doOutput = true //출력 사용
con.doInput = true //입력 사용
//문자열을 다운로드 받기 위한 스트림을 생성
val br =
BufferedReader(InputStreamReader(con.inputStream))
val sb: StringBuilder = StringBuilder()
//문자열을 읽어서 저장
while (true) {
val line = br.readLine() ?: break
sb.append(line.trim())
}
json = sb.toString()
//읽은 데이터 확인
Log.e("json", json!!)
//사용한 스트림과 연결 해제
br.close()
con.disconnect()
} catch (e: Exception) {
Log.e("다운로드 실패", e.message!!)
}
//json 파싱
if(json!!.trim().length > 0){
val data = JSONObject(json)
count = data.getInt("count")
val list = data.getJSONArray("list")
var i = 0
while (i < list.length()) {
val item = list.getJSONObject(i)
val movie = Movie()
movie.movieid = item.getInt("movieid")
movie.title = item.getString("title")
movie.subtitle = item.getString("subtitle")
movie.genre = item.getString("genre")
movie.rating = item.getDouble("rating")
movie.thumbnail = item.getString("thumbnail")
movie.link = item.getString("link")
movieList!!.add(movie)
i = i + 1
}
Log.e("파싱 결과 - count", "${count}")
Log.e("파싱 결과 - movieList", "${movieList.toString()}")
handler.sendEmptyMessage(0)
}
}
}
val handler : Handler = object: Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message){
movieAdapter!!.notifyDataSetChanged()
downloadview.visibility = View.GONE
th = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_movie_list)
movieList = mutableListOf<Movie>()
movieAdapter = MovieAdapter(
this, movieList!!, R.layout.movie_cell)
listview.adapter = movieAdapter
listview.setDivider(ColorDrawable(Color.RED))
listview.setDividerHeight(3)
// Listview 의 셀 클릭 이벤트 처리
// Parent 는 이벤트가 발생한 부모 뷰 - listview
// view 는 이벤트가 발생한 항목 뷰 - 셀을 조작하고자 할 때 이용
// position 은 셀의 인덱스 - 데이터를 찾아 올 때
// id 는 셀을 만들 때 넘겨준 id
listview.onItemClickListener = AdapterView.OnItemClickListener{
parent, view, position, id ->
// 데이터 찾아오기
val movie = movieList!!.get(position)
// 넘겨줄 데이터는 link
val link : String = movie.link!!
// 호출할 Activity 를 Intent 로 생성
val intent:Intent = Intent(this, LinkActivity::class.java)
intent.putExtra("link", link)
startActivity(intent)
}
// List View 의 스크롤 이벤트 처리
listview.setOnScrollListener(object : AbsListView.OnScrollListener{
override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) {
// 가장 아래에서 스크롤 했다면
if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && lastItemVisibleFlag) {
// 페이지 번호를 1 증가
pageno = pageno + 1
// 한 페이지 당 데이터 개수
val cnt = 10
// 모든 데이터를 가져 온 것이라면
if(pageno* cnt >= count!!) {
Toast.makeText(this@MovieListActivity,
"더이상 데이터 없음", Toast.LENGTH_LONG).show()
return
}
if (th != null) {
return
}
// 다운로드 시작
downloadview.visibility = View.VISIBLE
th = MovieThread()
th!!.start()
}
}
override fun onScroll(
view: AbsListView?,
firstVisibleItem: Int,
visibleItemCount: Int,
totalItemCount: Int
) {
// ListView 의 가장 하단에서 스크롤 했는지 판단
// 전체 아이템 개수가 0 보다 커야 하고
// 첫번째 보여지는 아이쳄과 보여져야 하는 아이템의 개수를 더한 값이
// 전체 아이템 개수보다 크다면 마지막에서 스크롤 한 것입니다.
lastItemVisibleFlag = totalItemCount > 0 &&
firstVisibleItem + visibleItemCount >= totalItemCount
}
})
if(th != null) {
return
}
downloadview.visibility = View.VISIBLE
th = MovieThread()
th!!.start()
}
}
리스트 뷰의 하단에서 아래로 스크롤 했을 때 데이터를 가져와서 출력
page 1 부터 시작하는 페이지 번호
cyberadam.cafe24.com/movie/list?page=1
하단에서 스크롤 할 때 마지막 페이지 인지 확인을 하고 마지막 페이지 라면 데이터를 더이상 가져오지 않고
마지막 페이지가 아니라면 페이지 번호를 1 증가 시켜서 데이터를 가져와서 출력하면 됩니다.
하단에서 스크롤 하는 경우는 대부분 이전 데이터를 가져오는 것이라서 List 에 추가할 때 뒤에 추가하는 경우가 많습니다.
1. MovieListActvity 클래스에 2개의 변수를 선언
// 현재 페이지 번호를 저장할 프로퍼티
var pageno = 1
// 마지막 페이지 여부를 저장할 플래그 프로퍼티
var lastItemVisibleFlag = false
2. onCreate 메소드에서 ListView 에서 스크롤이 발생한 경우 호출될 리스너를 등록
=> AbsListView.OnScrollListener 가 처리
// List View 의 스크롤 이벤트 처리
listview.setOnScrollListener(object : AbsListView.OnScrollListener{
override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) {
// 가장 아래에서 스크롤 했다면
if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && lastItemVisibleFlag) {
// 페이지 번호를 1 증가
pageno = pageno + 1
// 한 페이지 당 데이터 개수
val cnt = 10
// 모든 데이터를 가져 온 것이라면
if(pageno* cnt >= count!!) {
Toast.makeText(this@MovieListActivity,
"더이상 데이터 없음", Toast.LENGTH_LONG).show()
return
}
if (th != null) {
return
}
// 다운로드 시작
downloadview.visibility = View.VISIBLE
th = MovieThread()
th!!.start()
}
}
override fun onScroll(
view: AbsListView?,
firstVisibleItem: Int,
visibleItemCount: Int,
totalItemCount: Int
) {
// ListView 의 가장 하단에서 스크롤 했는지 판단
// 전체 아이템 개수가 0 보다 커야 하고
// 첫번째 보여지는 아이쳄과 보여져야 하는 아이템의 개수를 더한 값이
// 전체 아이템 개수보다 크다면 마지막에서 스크롤 한 것입니다.
lastItemVisibleFlag = totalItemCount > 0 &&
firstVisibleItem + visibleItemCount >= totalItemCount
}
})
if(th != null) {
return
}
downloadview.visibility = View.VISIBLE
th = MovieThread()
th!!.start()
pull to refresh
=> ListView(RecyclerView) 에서 상단에서 하단으로 스크롤을 할 때 데이터를 새로고침 하는 기능
=> 안드로이드에서는 SwipeRefreshLayout 으로 이 기능을 제공
ListView 나 RecyclerView 를 SwipeRefreshLayout 안에 삽입을 합니다.
=> SwipeRefreshLayout 의 OnRefreshListener 를 등록하고 이 리스너의 onRefresh 메소드에서 데이터를 가져와서 새로고침하는 코드를 추가하면 됩니다.
새로 고침이 끝나면 반드시 SwipeRefreshLayout 를 setRefreshing(false)를 호출
1. build.gradle(모듈) 파일의 dependencies 추가
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
추가하고 Synch Now
activity_movie_list.xml
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
3. SwipeLayout 의 OnRefreshListener 를 등록하고 업데이트 코드를 작성
MovieListActivity.kt
package com.example.adapterview
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
import android.widget.AbsListView
import android.widget.AdapterView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import kotlinx.android.synthetic.main.activity_movie_list.*
import org.json.JSONObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
class MovieListActivity : AppCompatActivity() {
//다운로드 받은 문자열을 저장하기 위한 프로퍼티
var json:String? = null
//다운로드 스레드를 위한 프로퍼티
var th : MovieThread? = null
//데이터 목록을 저장할 리스트
var movieList : MutableList<Movie>? = null
//데이터 개수를 저장할 변수
var count:Int? = null
//ListView에 출력하기 위한 Adapter
//var movieAdapter: ArrayAdapter<Movie>? = null
var movieAdapter: MovieAdapter? = null
// 현재 페이지 번호를 저장할 프로퍼티
var pageno = 1
// 마지막 페이지 여부를 저장할 플래그 프로퍼티
var lastItemVisibleFlag = false
inner class MovieThread : Thread() {
override fun run() {
try {
//다운로드 받을 주소 생성
var url: URL = URL("http://cyberadam.cafe24.com/movie/list?page=${pageno}")
//연결 객체 생성
val con =
url!!.openConnection() as HttpURLConnection
//옵션 설정
con.requestMethod = "GET" //전송 방식 선택
con.useCaches = false //캐시 사용 여부 설정
con.connectTimeout = 30000 //접속 시도 시간 설정
con.readTimeout = 3000 //읽는데 걸리는 시간 설정
con.doOutput = true //출력 사용
con.doInput = true //입력 사용
//문자열을 다운로드 받기 위한 스트림을 생성
val br =
BufferedReader(InputStreamReader(con.inputStream))
val sb: StringBuilder = StringBuilder()
//문자열을 읽어서 저장
while (true) {
val line = br.readLine() ?: break
sb.append(line.trim())
}
json = sb.toString()
//읽은 데이터 확인
Log.e("json", json!!)
//사용한 스트림과 연결 해제
br.close()
con.disconnect()
} catch (e: Exception) {
Log.e("다운로드 실패", e.message!!)
}
//json 파싱
if(json!!.trim().length > 0){
val data = JSONObject(json)
count = data.getInt("count")
val list = data.getJSONArray("list")
var i = 0
while (i < list.length()) {
val item = list.getJSONObject(i)
val movie = Movie()
movie.movieid = item.getInt("movieid")
movie.title = item.getString("title")
movie.subtitle = item.getString("subtitle")
movie.genre = item.getString("genre")
movie.rating = item.getDouble("rating")
movie.thumbnail = item.getString("thumbnail")
movie.link = item.getString("link")
movieList!!.add(0, movie) // 위에서 부터 업데이트를 위해 0
i = i + 1
}
Log.e("파싱 결과 - count", "${count}")
Log.e("파싱 결과 - movieList", "${movieList.toString()}")
handler.sendEmptyMessage(0)
}
}
}
val handler : Handler = object: Handler(Looper.getMainLooper()){
override fun handleMessage(msg: Message){
movieAdapter!!.notifyDataSetChanged()
downloadview.visibility = View.GONE
th = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_movie_list)
movieList = mutableListOf<Movie>()
movieAdapter = MovieAdapter(
this, movieList!!, R.layout.movie_cell)
listview.adapter = movieAdapter
listview.setDivider(ColorDrawable(Color.RED))
listview.setDividerHeight(3)
// Listview 의 셀 클릭 이벤트 처리
// Parent 는 이벤트가 발생한 부모 뷰 - listview
// view 는 이벤트가 발생한 항목 뷰 - 셀을 조작하고자 할 때 이용
// position 은 셀의 인덱스 - 데이터를 찾아 올 때
// id 는 셀을 만들 때 넘겨준 id
listview.onItemClickListener = AdapterView.OnItemClickListener{
parent, view, position, id ->
// 데이터 찾아오기
val movie = movieList!!.get(position)
// 넘겨줄 데이터는 link
val link : String = movie.link!!
// 호출할 Activity 를 Intent 로 생성
val intent:Intent = Intent(this, LinkActivity::class.java)
intent.putExtra("link", link)
startActivity(intent)
}
// List View 의 스크롤 이벤트 처리
listview.setOnScrollListener(object : AbsListView.OnScrollListener{
override fun onScrollStateChanged(view: AbsListView?, scrollState: Int) {
// 가장 아래에서 스크롤 했다면
if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && lastItemVisibleFlag) {
// 페이지 번호를 1 증가
pageno = pageno + 1
// 한 페이지 당 데이터 개수
val cnt = 10
// 모든 데이터를 가져 온 것이라면
if(pageno* cnt >= count!!) {
Toast.makeText(this@MovieListActivity,
"더이상 데이터 없음", Toast.LENGTH_LONG).show()
return
}
if (th != null) {
return
}
// 다운로드 시작
downloadview.visibility = View.VISIBLE
th = MovieThread()
th!!.start()
}
}
override fun onScroll(
view: AbsListView?,
firstVisibleItem: Int,
visibleItemCount: Int,
totalItemCount: Int
) {
// ListView 의 가장 하단에서 스크롤 했는지 판단
// 전체 아이템 개수가 0 보다 커야 하고
// 첫번째 보여지는 아이쳄과 보여져야 하는 아이템의 개수를 더한 값이
// 전체 아이템 개수보다 크다면 마지막에서 스크롤 한 것입니다.
lastItemVisibleFlag = totalItemCount > 0 &&
firstVisibleItem + visibleItemCount >= totalItemCount
}
})
// SwipeRefreshLayout 에서 가장 상단에서 아래로 일정 시간 동안 드래그 했을 때 수행할 리스너 등록
swipe_layout.setOnRefreshListener(SwipeRefreshLayout.OnRefreshListener {
pageno = pageno + 1
val cnt = 10
if(pageno*cnt >= count!!) {
Toast.makeText(this@MovieListActivity,
"더이상 데이터 없음", Toast.LENGTH_LONG).show()
} else{
if(th != null) {
} else {
downloadview.visibility = View.VISIBLE
th = MovieThread()
th!!.start()
swipe_layout.setRefreshing(false)
}
}
})
if(th != null) {
return
}
downloadview.visibility = View.VISIBLE
th = MovieThread()
th!!.start()
}
}
Activity Bar
화면 상단의 바
기본적으로는 Activity 에 설정된 문자열이 출력이 됩니다.
필요하면 Action Button 이나 Menu 등이 출력 되도록 할 수 있습니다.
테마 설정을 이용해서 보이지 않도록 할 수 있습니다.
windowNoTitle 을 true 로 설정하고 windowActionBar 를 false 로 설정 하면 액션바를 숨길 수 있습니다.
동적 (코드)으로 숨기거나 보이도록 할 수 있습니다.
val actionBar:ActionBar = getSupportActionBar()
actionBar.show() 또는 hide()
=> 화면에 Floating 하도록 설정 가능
테마 설정에서 windowActionBarOverlay 를 true로 설정
색상을 진한 것을 선택하면 Content 영역이 보이지 않게 됩니다.
Custom View
View 클래스로부터 상속받는 클래스를 직접 생성
=> 이 클래스를 사용하는 경우는 그리기를 직접 구현하거나 API 에서 제공하지 않는 모양의 뷰를 만들 목적입니다.
customViewActivity.kt
package com.example.adapterview
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.MotionEvent
import android.view.View
// 점의 좌표를 저장할 클래스
// Float 로 x, y 를 갖고 Boolean 타입으로 Draw 를 갖는 클래스
class Vertex internal constructor(var x:Float, var y:Float, var Draw:Boolean)
class CustomViewActivity : AppCompatActivity() {
// 점의 좌표들을 소유할 List
var arVertex: MutableList<Vertex>? = null
// 외부에 있는 프로퍼티를 사용하기 위해서 inner class 로 설정 합니다.
inner class MyCustomView(context: Context): View(context){
// 그리기 정보를 저장할 Paint 프로퍼티
var mPaint : Paint
public override fun onDraw(canvas: Canvas){
// 화면에 그림을 그려주는 메소드드
canvas.drawColor(Color.LTGRAY)
// 선 그리기
for(i in arVertex!!.indices) {
if(arVertex!!.get(i).Draw){
canvas.drawLine(
arVertex!!.get(i-1).x, arVertex!!.get(i-1).y,
arVertex!!.get(i).x, arVertex!!.get(i).y, mPaint)
}
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if(event.action == MotionEvent.ACTION_DOWN) {
arVertex!!.add(Vertex(event.x, event.y, false))
return true
}
if(event.action == MotionEvent.ACTION_MOVE) {
arVertex!!.add(Vertex(event.x, event.y, true))
invalidate()
return true
}
return false
}
init {
mPaint = Paint()
mPaint.setColor(Color.BLACK)
mPaint.setStrokeWidth(10f)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContentView(R.layout.activity_custom_view)
val myView = MyCustomView(this)
setContentView(myView)
arVertex = mutableListOf<Vertex>()
}
}
'안드로이드' 카테고리의 다른 글
안드로이드 : 구글 맵과 서울시 공공 Open API 연동 (0) | 2020.11.11 |
---|---|
안드로이드 : (0) | 2020.11.10 |
안드로이드 : 영화 소개 페이지 웹 크롤링 (0) | 2020.11.05 |
안드로이드 : 웹 서버 연결 (0) | 2020.11.04 |
안드로이드 : Thread 와 Handler / 서버 통신 (0) | 2020.11.03 |