와챠의 우당탕탕 코딩 일기장
[안드로이드] 가로 슬라이드 리사이클러뷰 본문
이건 걍... 말할 것도 없음
android:orientation="horizontal" 만 추가하면 됨....
끝임........
덜덜
갑자기 가로 슬라이드 리사이클러뷰가 만들고 싶어서 만들어본 건데
너무 간단해서... 머쓱하다.
그래도 만들었으니 일단 올려봄.
그리고 레이아웃 매니저 설정도 좀 다르게 해줬다.
그동안은 RecyclerView의 layoutManager 설정해주는 거를 kt에서 코드로
weatherRecyclerView1.layoutManager = LinearLayoutManager(this@MainActivity)
이렇게 했는데
이번엔 xml에서 아래코드를 추가하는 방식으로 해봤다. 코드도 깔끔해지고 좋은 듯?
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
실행 결과 : 분홍색 부분이 가로 슬라이드

하늘색 부분 및 날씨 정보 가져와서 뷰 설정하는 부분은 여기서
https://min-wachya.tistory.com/163
[안드로이드] 최신 기상청 단기예보 API 활용하기(초단기예보, Json)
올해 1학기 때 과제로 기상청 동네 예보 API를 사용한 적이 있었는데... 7월 초에 이런 메일이 왔다. 동네 예보 API가 종료된다고... 물론 단기 예보 단위가 상세화 되는 건 좋다~ 기존에 있던 코드
min-wachya.tistory.com
avtivity_main.xml

<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout 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" | |
android:gravity="center" | |
android:orientation="vertical"> | |
<TextView | |
android:id="@+id/tvDate" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:text="mm/dd의 날씨" | |
android:textSize="30sp" | |
android:textColor="@color/black" | |
android:layout_margin="10dp" | |
android:gravity="center"/> | |
<TextView | |
android:id="@+id/tvError" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:text="에러 : " | |
android:textSize="30sp" | |
android:textColor="@color/black" | |
android:layout_margin="10dp" | |
android:gravity="center" | |
android:visibility="gone"/> | |
<androidx.recyclerview.widget.RecyclerView | |
android:id="@+id/weatherRecyclerView1" | |
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | |
android:orientation="horizontal" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content"/> | |
<androidx.recyclerview.widget.RecyclerView | |
android:id="@+id/weatherRecyclerView2" | |
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | |
android:layout_width="match_parent" | |
android:layout_height="0dp" | |
android:layout_weight="1"/> | |
<Button | |
android:id="@+id/btnRefresh" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginTop="10dp" | |
android:text="새로고침"/> | |
</LinearLayout> |
item_list_weather1.xml

<?xml version="1.0" encoding="utf-8"?> | |
<LinearLayout | |
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:background="#ffccdd" | |
android:orientation="vertical" | |
android:gravity="center" | |
android:padding="10dp" | |
android:layout_margin="10dp"> | |
<ImageView | |
android:id="@+id/imgWeather" | |
android:layout_width="60dp" | |
android:layout_height="60dp" | |
android:src="@drawable/sun"/> | |
<TextView | |
android:id="@+id/tvTemp" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="온도" | |
android:textColor="@color/black" | |
android:textSize="24sp"/> | |
<TextView | |
android:id="@+id/tvHumidity" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="습도" | |
android:textColor="@color/black" | |
android:textSize="14sp"/> | |
<TextView | |
android:id="@+id/tvTime" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="HHmm" | |
android:textColor="@color/black" | |
android:textSize="30sp"/> | |
</LinearLayout> |
MainActivity.kt : 날씨 api 정보 받아와서 리사이클러뷰 설정하기
package com.example.myweathertest2 | |
import android.graphics.Point | |
import androidx.appcompat.app.AppCompatActivity | |
import android.os.Bundle | |
import android.os.Looper | |
import android.util.Log | |
import android.view.View | |
import android.widget.Button | |
import android.widget.TextView | |
import android.widget.Toast | |
import androidx.recyclerview.widget.LinearLayoutManager | |
import androidx.recyclerview.widget.RecyclerView | |
import com.example.myweathertest2.Adapter.WeatherAdapter1 | |
import com.example.myweathertest2.Adapter.WeatherAdapter2 | |
import com.example.myweathertest2.Model.ModelWeather | |
import com.google.android.gms.location.LocationCallback | |
import com.google.android.gms.location.LocationRequest | |
import com.google.android.gms.location.LocationResult | |
import com.google.android.gms.location.LocationServices | |
import retrofit2.Call | |
import retrofit2.Response | |
import java.text.SimpleDateFormat | |
import java.util.* | |
// 메인 액티비티 | |
class MainActivity : AppCompatActivity() { | |
lateinit var weatherRecyclerView1 : RecyclerView // 날씨 리사이클러 뷰(가로 슿라이드, 분홍) | |
lateinit var weatherRecyclerView2 : RecyclerView // 날씨 리사이클러 뷰(세로 슿라이드, 하늘) | |
lateinit var tvDate : TextView | |
lateinit var base_date : String // 발표 일자 | |
lateinit var base_time : String // 발표 시각 | |
private var curPoint : Point? = null // 현재 위치의 격자 좌표를 저장할 포인트 | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
tvDate = findViewById(R.id.tvDate) // 오늘 날짜 텍스트뷰 | |
weatherRecyclerView1 = findViewById(R.id.weatherRecyclerView1) // 날씨 리사이클러 뷰(가로 슿라이드, 분홍) | |
weatherRecyclerView2 = findViewById(R.id.weatherRecyclerView2) // 날씨 리사이클러 뷰(세로 슿라이드, 하늘) | |
val btnRefresh = findViewById<Button>(R.id.btnRefresh) // 새로고침 버튼 | |
// xml 에서 설정 : app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | |
// // 리사이클러 뷰1(가로 슿라이드) 매니저 설정 | |
// weatherRecyclerView1.layoutManager = LinearLayoutManager(this@MainActivity) | |
// // 리사이클러 뷰2(세로 슿라이드) 매니저 설정 | |
// weatherRecyclerView2.layoutManager = LinearLayoutManager(this@MainActivity) | |
// 내 위치 위경도 가져와서 날씨 정보 설정하기 | |
requestLocation() | |
// <새로고침> 버튼 누를 때 위치 정보 & 날씨 정보 다시 가져오기 | |
btnRefresh.setOnClickListener { | |
requestLocation() | |
} | |
} | |
// 날씨 가져와서 설정하기 | |
private fun setWeather(nx : Int, ny : Int) { | |
// 준비 단계 : base_date(발표 일자), base_time(발표 시각) | |
// 현재 날짜, 시간 정보 가져오기 | |
val cal = Calendar.getInstance() | |
base_date = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(cal.time) // 현재 날짜 | |
val timeH = SimpleDateFormat("HH", Locale.getDefault()).format(cal.time) // 현재 시각 | |
val timeM = SimpleDateFormat("HH", Locale.getDefault()).format(cal.time) // 현재 분 | |
// API 가져오기 적당하게 변환 | |
base_time = Common().getBaseTime(timeH, timeM) | |
// 현재 시각이 00시이고 45분 이하여서 baseTime이 2330이면 어제 정보 받아오기 | |
if (timeH == "00" && base_time == "2330") { | |
cal.add(Calendar.DATE, -1).toString() | |
base_date = SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(cal.time) | |
} | |
// 날씨 정보 가져오기 | |
// (한 페이지 결과 수 = 60, 페이지 번호 = 1, 응답 자료 형식-"JSON", 발표 날싸, 발표 시각, 예보지점 좌표) | |
val call = ApiObject.retrofitService.GetWeather(60, 1, "JSON", base_date, base_time, nx, ny) | |
// 비동기적으로 실행하기 | |
call.enqueue(object : retrofit2.Callback<WEATHER> { | |
// 응답 성공 시 | |
override fun onResponse(call: Call<WEATHER>, response: Response<WEATHER>) { | |
if (response.isSuccessful) { | |
// 날씨 정보 가져오기 | |
val it: List<ITEM> = response.body()!!.response.body.items.item | |
// 현재 시각부터 1시간 뒤의 날씨 6개를 담을 배열 | |
val weatherArr = arrayOf(ModelWeather(), ModelWeather(), ModelWeather(), ModelWeather(), ModelWeather(), ModelWeather()) | |
// 배열 채우기 | |
var index = 0 | |
val totalCount = response.body()!!.response.body.totalCount - 1 | |
for (i in 0..totalCount) { | |
index %= 6 | |
when(it[i].category) { | |
"PTY" -> weatherArr[index].rainType = it[i].fcstValue // 강수 형태 | |
"REH" -> weatherArr[index].humidity = it[i].fcstValue // 습도 | |
"SKY" -> weatherArr[index].sky = it[i].fcstValue // 하늘 상태 | |
"T1H" -> weatherArr[index].temp = it[i].fcstValue // 기온 | |
else -> continue | |
} | |
index++ | |
} | |
// 각 날짜 배열 시간 설정 | |
for (i in 0..5) weatherArr[i].fcstTime = it[i].fcstTime | |
// 리사이클러 뷰1(가로 슿라이드, 분홍)에 데이터 연결 | |
weatherRecyclerView1.adapter = WeatherAdapter1(weatherArr) | |
// 리사이클러 뷰2(세로 슿라이드, 하늘)에 데이터 연결 | |
weatherRecyclerView2.adapter = WeatherAdapter2(weatherArr) | |
// 토스트 띄우기 | |
Toast.makeText(applicationContext, it[0].fcstDate + ", " + it[0].fcstTime + "의 날씨 정보입니다.", Toast.LENGTH_SHORT).show() | |
} | |
} | |
// 응답 실패 시 | |
override fun onFailure(call: Call<WEATHER>, t: Throwable) { | |
val tvError = findViewById<TextView>(R.id.tvError) | |
tvError.text = "api fail : " + t.message.toString() + "\n 다시 시도해주세요." | |
tvError.visibility = View.VISIBLE | |
Log.d("api fail", t.message.toString()) | |
} | |
}) | |
} | |
// 내 현재 위치의 위경도를 격자 좌표로 변환하여 해당 위치의 날씨정보 설정하기 | |
private fun requestLocation() { | |
val locationClient = LocationServices.getFusedLocationProviderClient(this@MainActivity) | |
try { | |
// 나의 현재 위치 요청 | |
val locationRequest = LocationRequest.create() | |
locationRequest.run { | |
priority = LocationRequest.PRIORITY_HIGH_ACCURACY | |
interval = 60 * 1000 // 요청 간격(1초) | |
} | |
val locationCallback = object : LocationCallback() { | |
// 요청 결과 | |
override fun onLocationResult(p0: LocationResult?) { | |
p0?.let { | |
for (location in it.locations) { | |
// 현재 위치의 위경도를 격자 좌표로 변환 | |
curPoint = Common().dfs_xy_conv(location.latitude, location.longitude) | |
// 오늘 날짜 텍스트뷰 설정 | |
tvDate.text = SimpleDateFormat("MM월 dd일", Locale.getDefault()).format(Calendar.getInstance().time) + "날씨" | |
// nx, ny지점의 날씨 가져와서 설정하기 | |
setWeather(curPoint!!.x, curPoint!!.y) | |
} | |
} | |
} | |
} | |
// 내 위치 실시간으로 감지 | |
locationClient?.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper()) | |
} catch (e : SecurityException) { | |
e.printStackTrace() | |
} | |
} | |
} |
WeatherAdapter.kt : 받아온 날씨 정보 배열로 아이템 뷰 설정하기
package com.example.myweathertest2.Adapter | |
import android.view.LayoutInflater | |
import android.view.View | |
import android.view.ViewGroup | |
import android.widget.ImageView | |
import android.widget.TextView | |
import androidx.recyclerview.widget.RecyclerView | |
import com.example.myweathertest2.Model.ModelWeather | |
import com.example.myweathertest2.R | |
class WeatherAdapter1 (var items : Array<ModelWeather>) : RecyclerView.Adapter<WeatherAdapter1.ViewHolder>() { | |
// 뷰 홀더 만들어서 반환, 뷰릐 레이아웃은 list_item_weather1.xml | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WeatherAdapter1.ViewHolder { | |
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_list_weather1, parent, false) | |
return ViewHolder(itemView) | |
} | |
// 전달받은 위치의 아이템 연결 | |
override fun onBindViewHolder(holder: WeatherAdapter1.ViewHolder, position: Int) { | |
val item = items[position] | |
holder.setItem(item) | |
} | |
// 아이템 갯수 리턴 | |
override fun getItemCount() = items.count() | |
// 뷰 홀더 설정 | |
inner class ViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) { | |
fun setItem(item : ModelWeather) { | |
val imgWeather = itemView.findViewById<ImageView>(R.id.imgWeather) // 날씨 이미지 | |
val tvTemp = itemView.findViewById<TextView>(R.id.tvTemp) // 온도 | |
val tvHumidity = itemView.findViewById<TextView>(R.id.tvHumidity) // 습도 | |
val tvTime = itemView.findViewById<TextView>(R.id.tvTime) // 시각 | |
imgWeather.setImageResource(getWeatherImage(item.sky)) | |
tvTemp.text = item.temp + "°" | |
tvHumidity.text = "습도 : ${item.humidity}" | |
tvTime.text = item.fcstTime | |
} | |
} | |
fun getWeatherImage(sky : String) : Int { | |
// 하늘 상태 | |
return when(sky) { | |
"1" ->R.drawable.sun // 맑음 | |
"3" -> R.drawable.very_cloudy // 구름 많음 | |
"4" -> R.drawable.cloudy // 흐림 | |
else -> R.drawable.ic_launcher_foreground // 오류 | |
} | |
} | |
} |
리사이클러뷰 orientation를 horizontal로 바꾸니까 화면이
item0
item1
item2 <이게 아니라
item0 item1 item2 < 이렇게 된 거가ㅋㅋㅋ(당연함
신기햇음^^
'코딩 일기장 > Android(Kotlin)' 카테고리의 다른 글
[안드로이드] 이미지에서 색상 추출(Palette), toolbar 커스텀 (트위터 따라하기) (0) | 2021.08.15 |
---|---|
[안드로이드] 메일 보내기 (4) | 2021.08.13 |
[안드로이드] 사용자 위치 가져와서 날씨 정보 설정하기 (10) | 2021.07.30 |
[안드로이드] 최신 기상청 단기예보 API 활용하기(초단기예보, Json) (26) | 2021.07.30 |
[안드로이드] 갤러리에서 사진 가져오고 크롭하기(크기 조절하기) (6) | 2021.06.24 |