WebView
WebView(웹뷰)란 프레임워크에 내장된 웹 브라우저 컴포넌트로 View(뷰)의 형태로 앱에 임베딩 하는 것
즉, WebView(웹뷰)는 앱 내에 웹 브라우저를 넣는 것
일단 WebView를 사용하는 기본적인 방법부터 정리하자면, 아래의 순서와 같다.
1. build.gradle에 viewBinding 설정해준다.
buildFeatures {
viewBinding true
}
2. AndroidManifest.xml에 인터넷 퍼미션 추가
<uses-permission android:name="android.permission.INTERNET" />
3. xml에 webview영역 선언
<WebView
android:id="@+id/webView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
4. MainActivity에서 Webview 선언
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding=ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.webview.webViewClient = WebViewClient()
binding.webView.settings.javaScriptEnabled = true
binding.webview.loadUrl("https://www.naver.com")
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
}
private val onBackPressedCallback: OnBackPressedCallback =
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// 실행코드
}
}
webViewClient를 사용하는 이유는 기본 웹앱이 실행되기 때문이다.
예를 들면 내장되어 있는 크롬앱, 삼성브라우저 등등.. 이런 앱들이 실행되기때문에
내가 원하는 Web brower를 사용하기 위해서 webViewClient를 사용해서 Webview내에서 이루어지는 로딩같은 동작들을 수행해준다.
자바스크립트 사용 여부를 true로 설정해주고 Back 버튼 눌렀을경우 앱이 종료되는 것이 아니라 이전 페이지로 돌아가는 등.. 뒤로가기 버튼이 눌렸을 시 원하는 실행 코드를 작성해 주시면 됩니다.
버튼에 관련된건 이전 포스팅에도 간단하게 정리해 두었으니 참고하고 싶으신 분들은 참고하세요!
여기서부터는 예제관련된 글입니다.
완성도가 다소 떨어집니다...😅 웹뷰를 활용하는 것에 초점을 맞췄다는 점을 잊지 말하주세용..ㅎㅎ
패스트캠퍼스 강의를 수강하면서 소스를 참고해서 만들었습니다.

간략한 설명
- 내 블로그 화면에서 게시물에 들어갈 경우 그 값을 최근 방문 페이지로 넘겨줌, 최근 방문 페이지는 내 블로그 화면에서 게시물로 들어가지 않는 한 아무런 화면도 뜨지 않게 만들었음.
사실 이 부분은 xml을 따로 만들어서 다뤄주고 싶었으나.. 나의 귀차니즘 이슈로 인해..😅
- 내 블로그 화면에서 현재 페이지 저장하기 버튼을 누를 경우 값이 저장됨 (SharedPreferences를 사용해서 값을 저장해줬음)
- 최근 방문 페이지에서는 뒤로가기 기능이 필요없어서 따로 만들어주지 않았고 저장된 페이지 불러오기 버튼을 이용하여 언제든 저장된 페이지를 불러오게 만들어줬고, 내 블로그 화면에서 다른 게시물을 누른다면 다시 동기화 되게 만들어줬다. (동기화 과정은 Viewmodel을 사용해줬다.)
UI
- activity_main.xml
- fragment_webview.xml
activity_main.xml
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabLayout" />
fragment_webview.xml
<WebView
android:id="@+id/webView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/saveButton"
android:layout_width="0dp"
android:layout_height="50dp"
android:text="@string/save_bt"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/callButton"/>
<Button
android:id="@+id/callButton"
android:layout_width="0dp"
android:layout_height="50dp"
android:text="@string/call_last_bt"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/saveButton" />
MainActivity.kt
class MainActivity : AppCompatActivity() {
private val TAG = javaClass.simpleName
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.viewPager.adapter = ViewPagerAdapter(this)
val tabNameList: List<String> = listOf("내 블로그", "최근 방문 페이지")
TabLayoutMediator(binding.tabLayout, binding.viewPager){tab, position ->
run {
tab.text="${tabNameList[position]}"
}
}.attach()
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
}
private val onBackPressedCallback: OnBackPressedCallback =
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val currentFragment =
supportFragmentManager.fragments[binding.viewPager.currentItem]
if (currentFragment is WebViewFragment) {
if (currentFragment.canGoBack()) {
currentFragment.goBack()
} else {
super.handleOnBackCancelled()
}
} else {
super.handleOnBackCancelled()
}
}
}
}
MainActivity에서는 탭과 뷰 페이저에 관련된 것들을 정의해주고 backbutton을 눌렀을때 실행되는 코드를 작성해 주었다.
WebViewFragment에서만 back 버튼이 필요했기때문에 그게 완련된 것만 작성해 주었다.
ViewPagerAdapter.kt
class ViewPagerAdapter(private val mainActivity: MainActivity) : FragmentStateAdapter(mainActivity){
//Log 찍을 때 TAG값으로 사용하기 위해서 넣었던 코드
//var TAG: String = ViewPagerAdapter::class.java.simpleName
override fun getItemCount(): Int {
return 2
}
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> {
return WebViewFragment(position, "https://brcho.tistory.com/m/")
}
else -> {
return SecondWebViewFragment(position, "")
}
}
}
}
WebViewFragment.kt
내 블로그 Fragment, viewmodel로 화면이 바뀔때 마다 SecondWebViewFragment로 값 전달
class WebViewFragment(private val position: Int, private val webViewUrl: String) : Fragment() {
private lateinit var binding: FragmentWebviewBinding
var TAG: String = WebViewFragment::class.java.simpleName
lateinit var model: WebViewmodel
// lateinit 선언이유 : oncreate 이후에 뷰 바인딩이 나오기 때문에 nullable로 해주면 계속 확인해줘야함 null인지 아닌지 그래서 lateinit을 사용용
companion object {
const val SHARED_PREFERNCE = "HISTORY"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentWebviewBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.callButton.isEnabled = false
model = ViewModelProvider(requireActivity()).get(WebViewmodel::class.java)
val sharedPreferences = activity?.getSharedPreferences(SHARED_PREFERNCE, Context.MODE_PRIVATE)
binding.webView.webViewClient = WebviewClient(binding.progressbar) { url ->
model.sendWebView(url)
binding.saveButton.setOnClickListener {
sharedPreferences?.edit {
putString("tab0", url)
}
Toast.makeText(activity, "페이지가 저장되었습니다.", Toast.LENGTH_LONG).show()
}
}
binding.webView.settings.javaScriptEnabled = true
binding.webView.loadUrl(webViewUrl)
}
fun canGoBack(): Boolean {
return binding.webView.canGoBack()
}
fun goBack() {
binding.webView.goBack()
}
}
WebviewClient.kt
내 블로그에서 url이 바뀔 때 값을 저장
class WebviewClient(
private val progressbar: ProgressBar,
private val saveData:(String) -> Unit,) : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
if(request != null && request.url.toString().contains("brcho.tistory.com/m/")){
saveData(request.url.toString())
}
return super.shouldOverrideUrlLoading(view, request)
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
progressbar.visibility = View.GONE
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
progressbar.visibility = View.VISIBLE
}
}
WebViewmodel.kt
class WebViewmodel: ViewModel() {
val sendView = MutableLiveData<String>()
fun sendWebView(url: String){
sendView.value = url
}
}
SecondWebViewFragment.kt
Viewmodel을 통해 받은 값으로 WebView를 화면에 뿌려줌
class SecondWebViewFragment(private val position: Int, private val webViewUrl: String) : Fragment() {
private lateinit var binding: FragmentWebviewBinding
var TAG: String = SecondWebViewFragment::class.java.simpleName
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentWebviewBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.saveButton.isEnabled = false
binding.progressbar.isGone = true
if(webViewUrl.isNullOrEmpty()){
Toast.makeText(activity, "저장된 페이지가 없습니다.", Toast.LENGTH_LONG).show()
}
val model = ViewModelProvider(requireActivity())[WebViewmodel::class.java]
model.sendView.observe(viewLifecycleOwner) { binding.webView.loadUrl(it) }
binding.callButton.setOnClickListener {
val sharedPreferences = activity?.getSharedPreferences(WebViewFragment.SHARED_PREFERNCE, Context.MODE_PRIVATE)
val url = sharedPreferences?.getString("tab0","")
binding.webView.loadUrl(url.toString())
}
}
}
https://github.com/whdudtjs97/AndroidProject/tree/main/Webview
'Android' 카테고리의 다른 글
[Android/안드로이드] 액티비티(Activity)를 팝업(PopUp)으로 만들기 (1) | 2024.07.16 |
---|---|
[안드로이드/Android] Flow layout 사용하기 (0) | 2024.04.04 |
[Android/안드로이드] onBackPressed() deprecated (0) | 2024.04.01 |
[Android/안드로이드] @SerializedName 어노테이션(Annotation)이란? (0) | 2023.05.09 |
[Android/안드로이드] View Binding (뷰 바인딩) (0) | 2022.12.03 |