-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Doodung step2 #7
base: doodung
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package place.pic.android.plus | ||
|
||
import android.widget.ImageView | ||
import androidx.databinding.BindingAdapter | ||
import com.bumptech.glide.Glide | ||
|
||
object BindingAdapter { | ||
@BindingAdapter("setImageUrl") | ||
@JvmStatic | ||
fun loadImage(imageView: ImageView, url: String) { | ||
Glide.with(imageView.context) | ||
.load(url) | ||
.circleCrop() | ||
.into(imageView) | ||
} | ||
|
||
@BindingAdapter("setButtonChange") | ||
@JvmStatic | ||
fun setButtonChange(button: ImageView, compass: Boolean) { | ||
when (compass) { | ||
true -> button.setBackgroundResource(R.drawable.ic_baseline_clear_24) | ||
else -> button.setBackgroundResource(R.drawable.ic_baseline_search_24) | ||
} | ||
} | ||
|
||
/* | ||
@BindingAdapter("TextChangeWatcher") | ||
@JvmStatic | ||
fun onEditTextWatcher(): TextWatcher { | ||
return object : TextWatcher { | ||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} | ||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} | ||
override fun afterTextChanged(s: Editable) { | ||
} | ||
} | ||
} | ||
|
||
@BindingAdapter("bindRecyclerView") | ||
@JvmStatic | ||
fun bindRecyclerView(recyclerView: RecyclerView, searchUserData: MutableList<SearchUserData>?) | ||
{ | ||
if (recyclerView.adapter != null) { | ||
with(recyclerView.adapter as SearchUserAdapter) { | ||
submitList(searchUserData) | ||
} | ||
} | ||
} | ||
*/ | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package place.pic.android.plus | ||
|
||
import android.util.Log | ||
import androidx.annotation.MainThread | ||
import androidx.lifecycle.LifecycleOwner | ||
import androidx.lifecycle.MutableLiveData | ||
import androidx.lifecycle.Observer | ||
import java.util.concurrent.atomic.AtomicBoolean | ||
|
||
open class SingleLiveEvent<T> : MutableLiveData<T>() { | ||
|
||
private val mPending = AtomicBoolean(false) | ||
|
||
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) { | ||
if (hasActiveObservers()) { | ||
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.") | ||
} | ||
|
||
super.observe( | ||
owner, | ||
Observer { t -> | ||
if (mPending.compareAndSet(true, false)) { | ||
observer.onChanged(t) | ||
} | ||
} | ||
) | ||
} | ||
|
||
@MainThread | ||
override fun setValue(t: T?) { | ||
mPending.set(true) | ||
super.setValue(t) | ||
} | ||
|
||
@MainThread | ||
fun call() { | ||
value = null | ||
} | ||
|
||
companion object { | ||
private val TAG = "SingleLiveEvent" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package place.pic.android.plus.data.remote | ||
|
||
import place.pic.android.plus.data.remote.response.ResponseUserSearch | ||
import retrofit2.http.GET | ||
import retrofit2.http.Query | ||
|
||
interface GithubService { | ||
@GET("/search/users") | ||
suspend fun userList( | ||
@Query("q") param: String? | ||
): ResponseUserSearch | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package place.pic.android.plus.data.remote | ||
|
||
import retrofit2.Retrofit | ||
import retrofit2.converter.gson.GsonConverterFactory | ||
|
||
object RetrofitBuilder { | ||
private const val baseUrl = "https://api.github.com" | ||
|
||
private var retrofit = Retrofit.Builder().baseUrl(baseUrl) | ||
.addConverterFactory(GsonConverterFactory.create()) | ||
.build() | ||
|
||
var service: GithubService = retrofit.create(GithubService::class.java) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package place.pic.android.plus.data.remote.response | ||
|
||
import java.io.Serializable | ||
|
||
data class ResponseUserSearch( | ||
val total_count: Int, | ||
val incomplete_results: Boolean, | ||
val items: List<SearchUserData> | ||
) : Serializable | ||
|
||
data class SearchUserData( | ||
val login: String, | ||
val avatar_url: String, | ||
val html_url: String | ||
) : Serializable |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package place.pic.android.plus.detail | ||
|
||
import android.content.Intent | ||
import android.net.Uri | ||
import android.os.Bundle | ||
import androidx.appcompat.app.AppCompatActivity | ||
import com.bumptech.glide.Glide | ||
import place.pic.android.plus.data.remote.response.SearchUserData | ||
import place.pic.android.plus.databinding.ActivityDetailUserBinding | ||
|
||
class DetailUserActivity : AppCompatActivity() { | ||
private lateinit var binding: ActivityDetailUserBinding | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
binding = ActivityDetailUserBinding.inflate(layoutInflater) | ||
setContentView(binding.root) | ||
setBindingUserData() | ||
} | ||
|
||
private fun setBindingUserData() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 일단 이 함수가 하는 일은 무엇일까? 보다 간단한 이름이 있지는 않을지 고민해보면 좋을 것 같다는 생각이 들어 보다 간단한 이름을 생각해보면 이 함수가 하는 일이 과연 엑티비티가 해야할 역할에 포함되는지 생각해볼 수 있을 것 같아 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵! 수정하게씁니다! |
||
val user = intent.getSerializableExtra("user") as SearchUserData | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
지금 요구 사항에서는 필요 없는 부분 이지만 만약 DetailUserActivity 가 user 라는 정보를 받지 못하고 실행되었다면 어떻게 되어야 할지 처리해주는 것도 고려해봐야 하지 않을까? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 넵! 반영하겠습니다. |
||
|
||
with(binding) { | ||
tvUser.text = user.login | ||
|
||
Glide.with(this@DetailUserActivity) | ||
.load(user.avatar_url) | ||
.circleCrop() | ||
.into(imgUser) | ||
|
||
btnUser.setOnClickListener { | ||
val webIntent = | ||
Intent(Intent.ACTION_VIEW, Uri.parse(user.html_url)) | ||
startActivity(webIntent) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package place.pic.android.plus.search | ||
|
||
import android.content.Intent | ||
import android.os.Bundle | ||
import android.text.Editable | ||
import android.text.TextWatcher | ||
import android.view.KeyEvent | ||
import android.view.View | ||
import android.view.inputmethod.EditorInfo | ||
import android.widget.TextView | ||
import androidx.activity.viewModels | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.databinding.DataBindingUtil | ||
import androidx.lifecycle.lifecycleScope | ||
import kotlinx.coroutines.launch | ||
import place.pic.android.plus.R | ||
import place.pic.android.plus.data.remote.response.SearchUserData | ||
import place.pic.android.plus.databinding.ActivitySearchUserBinding | ||
import place.pic.android.plus.detail.DetailUserActivity | ||
import place.pic.android.plus.search.adapter.SearchUserAdapter | ||
import place.pic.android.plus.search.viewmodel.SearchUserViewModel | ||
|
||
class SearchUserActivity : AppCompatActivity() { | ||
private lateinit var binding: ActivitySearchUserBinding | ||
private val searchUserViewModel: SearchUserViewModel by viewModels() | ||
private lateinit var searchUserAdapter: SearchUserAdapter | ||
private val userList = mutableListOf<SearchUserData>() | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
binding = DataBindingUtil.setContentView(this, R.layout.activity_search_user) | ||
searchUserAdapter = SearchUserAdapter() | ||
binding.searchUserActivity = searchUserViewModel | ||
binding.lifecycleOwner = this | ||
binding.rvUserSearch.adapter = searchUserAdapter | ||
|
||
searchUser() | ||
changeButton() | ||
deleteText() | ||
gotoDetail() | ||
} | ||
|
||
private fun searchUser() { | ||
searchUserViewModel.recyclerListData.observe(this) { | ||
searchUserAdapter.submitList(it) | ||
} | ||
|
||
binding.etUserSearch.setOnEditorActionListener(object : TextView.OnEditorActionListener { | ||
override fun onEditorAction(v: TextView?, actionId: Int, event: KeyEvent?): Boolean { | ||
if (actionId == EditorInfo.IME_ACTION_SEARCH) { | ||
lifecycleScope.launch { | ||
searchUserViewModel.requestUserData(binding.etUserSearch.text.toString()) | ||
} | ||
return true | ||
} | ||
return false | ||
} | ||
}) | ||
} | ||
|
||
// livedata 쓰기 | ||
private fun changeButton() { | ||
binding.etUserSearch.addTextChangedListener(object : TextWatcher { | ||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { | ||
binding.btnUserSearch.visibility = View.VISIBLE | ||
binding.btnUserSearchDelete.visibility = View.INVISIBLE | ||
} | ||
|
||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { | ||
} | ||
|
||
override fun afterTextChanged(p0: Editable?) { | ||
binding.btnUserSearch.visibility = View.INVISIBLE | ||
binding.btnUserSearchDelete.visibility = View.VISIBLE | ||
} | ||
}) | ||
} | ||
|
||
// Btn 바인딩으로빼기 | ||
private fun deleteText() { | ||
binding.btnUserSearchDelete.setOnClickListener { | ||
binding.etUserSearch.text.clear() | ||
} | ||
} | ||
|
||
// 여기 바꿔야함요 ㅎㅎ | ||
private fun gotoDetail() { | ||
searchUserAdapter.itemClick = object : SearchUserAdapter.ItemClick { | ||
override fun onClick(view: View, position: Int) { | ||
val intent = Intent(this@SearchUserActivity, DetailUserActivity::class.java) | ||
|
||
intent.putExtra("user", userList[position]) | ||
startActivity(intent) | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코틀린은 불변성을 권장하는 언어라는 특징이 있어 "권장" 이기 때문에 대부분의 경우에서는 불변성을 자동으로 제공해주지만 프로그래머에게 그 역할이 위임 되어있는 경우도 존재한다 생각해
여기선 큰 문제점은 보이지 않지만 앞으로 코틀린을 계속 다룬다면 한번은 생각해보고 넘어가면 좋을 것 같아
애초에 여기서 binding 변수는 반드시 프로퍼티로 존재할 이유가 없다고 생각해
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
옳습니다