-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Create ViewBinding sample app * Implement ViewBinding sample app * Add MainActivity instrumentation tests * Add FragmentBind and FragmentInflate instrumentation tests * Create README.md
- Loading branch information
1 parent
d49d4ad
commit 1cd8f1f
Showing
29 changed files
with
815 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
*.iml | ||
.gradle | ||
/local.properties | ||
/.idea/* | ||
.DS_Store | ||
/build | ||
/captures | ||
.externalNativeBuild | ||
.cxx | ||
local.properties |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# View Binding | ||
|
||
Android sample app to learn about view binding in Android. It mainly showcases: | ||
- Enabling view binding through the app's build file. | ||
- Setting up binding instances in activities. | ||
- Setting up binding instances in Fragments using both the `inflate(LayoutInflater)` and `bind(View)` static methods. | ||
- Accessing and interacting with views through the binding instances. | ||
- Setting up layout files differently for different configurations, and accessing them through the binding instances with their nullable references. | ||
|
||
![View binding sample - inflate](https://github.com/husaynhakeem/android-playground/blob/master/ViewBindingSample/art/view_binding_portrait.png) | ||
![View binding sample - bind](https://github.com/husaynhakeem/android-playground/blob/master/ViewBindingSample/art/view_binding_landscape.png) | ||
|
||
### Resources to learn about view binding | ||
- [The official documentation on View Binding](https://developer.android.com/topic/libraries/view-binding) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
plugins { | ||
id "com.android.application" | ||
id "kotlin-android" | ||
} | ||
|
||
android { | ||
compileSdkVersion 30 | ||
|
||
defaultConfig { | ||
applicationId "com.husaynhakeem.viewbindingsample" | ||
minSdkVersion 21 | ||
targetSdkVersion 30 | ||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||
} | ||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_1_8 | ||
targetCompatibility JavaVersion.VERSION_1_8 | ||
} | ||
kotlinOptions { | ||
jvmTarget = "1.8" | ||
} | ||
viewBinding { | ||
enabled = true | ||
} | ||
} | ||
|
||
dependencies { | ||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" | ||
implementation "androidx.core:core-ktx:1.3.2" | ||
implementation "androidx.appcompat:appcompat:1.2.0" | ||
implementation "androidx.constraintlayout:constraintlayout:2.0.4" | ||
implementation "com.google.android.material:material:1.2.1" | ||
androidTestImplementation "androidx.test.ext:junit:1.1.2" | ||
androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0" | ||
androidTestImplementation "androidx.test:core-ktx:1.3.0" | ||
debugImplementation("androidx.fragment:fragment-testing:1.2.5") { | ||
exclude group: 'androidx.test', module: 'core' | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...ingSample/app/src/androidTest/java/com/husaynhakeem/viewbindingsample/FragmentBindTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.husaynhakeem.viewbindingsample | ||
|
||
import android.content.pm.ActivityInfo | ||
import androidx.fragment.app.testing.launchFragmentInContainer | ||
import androidx.test.espresso.Espresso.onView | ||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist | ||
import androidx.test.espresso.assertion.ViewAssertions.matches | ||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed | ||
import androidx.test.espresso.matcher.ViewMatchers.withId | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class FragmentBindTest { | ||
|
||
@Test | ||
fun doNotShowSubtitle_inPortraitOrientation() { | ||
// Arrange | ||
val scenario = launchFragmentInContainer { FragmentBind() } | ||
|
||
// Act | ||
scenario.onFragment { fragment -> | ||
fragment.requireActivity().requestedOrientation = | ||
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT | ||
} | ||
|
||
// Assert | ||
onView(withId(R.id.subtitle)).check(doesNotExist()) | ||
} | ||
|
||
@Test | ||
fun showSubtitle_inLandscapeOrientation() { | ||
// Arrange | ||
val scenario = launchFragmentInContainer { FragmentBind() } | ||
|
||
// Act | ||
scenario.onFragment { fragment -> | ||
fragment.requireActivity().requestedOrientation = | ||
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE | ||
} | ||
|
||
// Assert | ||
onView(withId(R.id.subtitle)).check(matches(isDisplayed())) | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
...Sample/app/src/androidTest/java/com/husaynhakeem/viewbindingsample/FragmentInflateTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.husaynhakeem.viewbindingsample | ||
|
||
import android.os.Build | ||
import androidx.fragment.app.testing.launchFragmentInContainer | ||
import androidx.test.espresso.Espresso.onView | ||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist | ||
import androidx.test.espresso.assertion.ViewAssertions.matches | ||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed | ||
import androidx.test.espresso.matcher.ViewMatchers.withId | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import org.junit.Assume.assumeTrue | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class FragmentInflateTest { | ||
|
||
@Test | ||
fun doNotShowSubtitle_onApisBelow23() { | ||
// Arrange | ||
assumeTrue(Build.VERSION.SDK_INT < 23) | ||
launchFragmentInContainer { FragmentInflate() } | ||
|
||
// Assert | ||
onView(withId(R.id.subtitle)).check(doesNotExist()) | ||
} | ||
|
||
@Test | ||
fun showSubtitle_onApis23AndAbove() { | ||
// Arrange | ||
assumeTrue(Build.VERSION.SDK_INT >= 23) | ||
launchFragmentInContainer { FragmentInflate() } | ||
|
||
// Assert | ||
onView(withId(R.id.subtitle)).check(matches(isDisplayed())) | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
...ingSample/app/src/androidTest/java/com/husaynhakeem/viewbindingsample/MainActivityTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.husaynhakeem.viewbindingsample | ||
|
||
import androidx.test.espresso.Espresso.onView | ||
import androidx.test.espresso.action.ViewActions.click | ||
import androidx.test.espresso.assertion.ViewAssertions.matches | ||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed | ||
import androidx.test.espresso.matcher.ViewMatchers.withId | ||
import androidx.test.ext.junit.rules.ActivityScenarioRule | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class MainActivityTest { | ||
|
||
@get:Rule | ||
val rule = ActivityScenarioRule(MainActivity::class.java) | ||
|
||
@Test | ||
fun showBindFragment_whenBindFragmentButtonClicked() { | ||
// Act | ||
onView(withId(R.id.bindFragmentButton)).perform(click()) | ||
|
||
// Assert | ||
onView(withId(R.id.bindFragmentRoot)).check(matches(isDisplayed())) | ||
} | ||
|
||
@Test | ||
fun showInflateFragment_whenInflateFragmentButtonClicked() { | ||
// Act | ||
onView(withId(R.id.inflateFragmentButton)).perform(click()) | ||
|
||
// Assert | ||
onView(withId(R.id.inflateFragmentRoot)).check(matches(isDisplayed())) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
package="com.husaynhakeem.viewbindingsample"> | ||
|
||
<application | ||
android:allowBackup="true" | ||
android:label="@string/app_name" | ||
android:supportsRtl="true" | ||
android:theme="@style/Theme.ViewBindingSample"> | ||
<activity android:name=".MainActivity"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.MAIN" /> | ||
|
||
<category android:name="android.intent.category.LAUNCHER" /> | ||
</intent-filter> | ||
</activity> | ||
</application> | ||
|
||
</manifest> |
37 changes: 37 additions & 0 deletions
37
ViewBindingSample/app/src/main/java/com/husaynhakeem/viewbindingsample/FragmentBind.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.husaynhakeem.viewbindingsample | ||
|
||
import android.os.Bundle | ||
import android.view.View | ||
import androidx.fragment.app.Fragment | ||
import com.husaynhakeem.viewbindingsample.databinding.FragmentBindBinding | ||
|
||
class FragmentBind : Fragment(R.layout.fragment_bind) { | ||
|
||
// Instance of the view binding. Should only be accessed while the view is valid, i.e. between | ||
// onViewCreated and onDestroyView. | ||
private var _binding: FragmentBindBinding? = null | ||
private val binding: FragmentBindBinding | ||
get() = _binding!! | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
// The layout has already been inflated. Get an instance of the binding class using the | ||
// static [bind] method. | ||
_binding = FragmentBindBinding.bind(view) | ||
|
||
// Get access to the title TextView, available in all layouts. | ||
binding.title.text = getString(R.string.bind_fragment_title) | ||
|
||
// [subtitle] is only available in landscape, and should be null in portrait orientation. | ||
binding.subtitle?.text = getString(R.string.bind_fragment_subtitle_landscape) | ||
} | ||
|
||
override fun onDestroyView() { | ||
super.onDestroyView() | ||
|
||
// Set the binding to null, since it should no longer be accessed after the view has been | ||
// destroyed. | ||
_binding = null | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
ViewBindingSample/app/src/main/java/com/husaynhakeem/viewbindingsample/FragmentInflate.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.husaynhakeem.viewbindingsample | ||
|
||
import android.os.Build | ||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.fragment.app.Fragment | ||
import com.husaynhakeem.viewbindingsample.databinding.FragmentInflateBinding | ||
|
||
class FragmentInflate : Fragment() { | ||
|
||
// Instance of the view binding. Should only be accessed while the view is valid, i.e. between | ||
// onCreateView and onDestroyView. | ||
private var _binding: FragmentInflateBinding? = null | ||
private val binding: FragmentInflateBinding | ||
get() = _binding!! | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, | ||
container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View? { | ||
// Set up instance of the binding class to use with this fragment by using the static | ||
// [inflate] method. | ||
_binding = FragmentInflateBinding.inflate(inflater) | ||
|
||
// Return the root of the binding, which references the LinearLayout. | ||
return binding.root | ||
} | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
// Get access to the title TextView, available in all layouts. | ||
binding.title.text = getString(R.string.inflate_fragment_title) | ||
|
||
// [subtitle] is only available in api levels 23 and above, and should be null in devices | ||
// running on older versions. | ||
val apiLevel = Build.VERSION.SDK_INT | ||
binding.subtitle?.text = getString(R.string.inflate_fragment_subtitle_pre_api_23, apiLevel) | ||
} | ||
|
||
override fun onDestroyView() { | ||
super.onDestroyView() | ||
|
||
// Set the binding to null, since it should no longer be accessed after the view has been | ||
// destroyed. | ||
_binding = null | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
ViewBindingSample/app/src/main/java/com/husaynhakeem/viewbindingsample/MainActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.husaynhakeem.viewbindingsample | ||
|
||
import android.os.Bundle | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.fragment.app.Fragment | ||
import com.husaynhakeem.viewbindingsample.databinding.ActivityMainBinding | ||
|
||
class MainActivity : AppCompatActivity() { | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
|
||
// Set up instance of the binding class to use within this Activity. | ||
val binding = ActivityMainBinding.inflate(layoutInflater) | ||
|
||
// Set the root view of the binding as the active view on the screen. | ||
setContentView(binding.root) | ||
|
||
// Set OnClickListeners on the buttons | ||
binding.bindFragmentButton.setOnClickListener { | ||
switchFragment(FragmentBind()) | ||
} | ||
binding.inflateFragmentButton.setOnClickListener { | ||
switchFragment(FragmentInflate()) | ||
} | ||
|
||
// Initially show Bind fragment | ||
if (savedInstanceState == null) { | ||
setFragment(FragmentBind()) | ||
} | ||
} | ||
|
||
private fun setFragment(fragment: Fragment) { | ||
supportFragmentManager.beginTransaction() | ||
.add(R.id.content, fragment) | ||
.commit() | ||
} | ||
|
||
private fun switchFragment(fragment: Fragment) { | ||
supportFragmentManager.beginTransaction() | ||
.replace(R.id.content, fragment) | ||
.commit() | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
ViewBindingSample/app/src/main/res/layout-land/fragment_bind.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:tools="http://schemas.android.com/tools" | ||
android:id="@+id/bindFragmentRoot" | ||
android:layout_width="match_parent" | ||
android:layout_height="match_parent" | ||
android:gravity="center" | ||
android:orientation="vertical"> | ||
|
||
<com.google.android.material.textview.MaterialTextView | ||
android:id="@+id/title" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline4" | ||
tools:text="Bind Fragment" /> | ||
|
||
<com.google.android.material.textview.MaterialTextView | ||
android:id="@+id/subtitle" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" | ||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" | ||
tools:text="Landscape" /> | ||
|
||
</LinearLayout> |
Oops, something went wrong.