-
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 RenderEffect sample app * Implement Blur effect * Implement color filter effect * Create main screen that lists the different effects the app showcases * Implement offset effect * Update Blue effect example to add chips dynamically * Add sample demos * Resize the sample demo gifs * Create README.md
- Loading branch information
1 parent
d5f6f06
commit 6f24287
Showing
32 changed files
with
1,149 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,15 @@ | ||
*.iml | ||
.gradle | ||
/local.properties | ||
/.idea/caches | ||
/.idea/libraries | ||
/.idea/modules.xml | ||
/.idea/workspace.xml | ||
/.idea/navEditor.xml | ||
/.idea/assetWizardSettings.xml | ||
.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,18 @@ | ||
# Render effects | ||
|
||
Android sample app to learn about the [`RenderEffect`](https://developer.android.com/reference/android/graphics/RenderEffect) API, which was recently introduced in Android 12. | ||
|
||
A `RenderEffect` corresponds to a visual effect that can be applied to render a part of the UI, a [`RenderNode`](https://developer.android.com/reference/android/graphics/RenderNode). It's a structure used to record draw operations, and can store and apply render properties when drawn. `RenderNode`s are useful to divide up the rendering of a scene into multiple smaller pieces. This allows updating them individually, which is more optimal/cheaper. | ||
|
||
`View`s in Android internally use `RenderNode`s, this allows for hardware accelerated rendering, meaning that UI hierarchies are rendered using the GPU, instead ofthe CPU. A `RenderEffect` can be applied to a View by calling [`View.setRenderEffect(RenderEffect)`](https://developer.android.com/reference/android/view/View#setRenderEffect(android.graphics.RenderEffect)). | ||
|
||
![blur-effect](https://github.com/husaynhakeem/android-playground/blob/master/RenderEffectSample/art/blur-effect.gif) | ||
![color-filter-effect](https://github.com/husaynhakeem/android-playground/blob/master/RenderEffectSample/art/color-filter-effect.gif) | ||
![offset-effect](https://github.com/husaynhakeem/android-playground/blob/master/RenderEffectSample/art/offset-effect.gif) | ||
|
||
The app mainly showcases: | ||
- Appliying blur effects on a View. | ||
- Applying color filter effects on a View. | ||
- Applying offset effects on a View. | ||
|
||
The sample should be run on a device/an emulator running Android 12, since the `RenderEffect` API was only introduced in Android S. |
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,31 @@ | ||
plugins { | ||
id 'com.android.application' | ||
id 'kotlin-android' | ||
} | ||
|
||
android { | ||
compileSdkVersion "android-S" | ||
defaultConfig { | ||
applicationId "com.husaynhakeem.rendereffectsample" | ||
minSdkVersion "S" | ||
targetSdkVersion "S" | ||
versionCode 1 | ||
} | ||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_1_8 | ||
targetCompatibility JavaVersion.VERSION_1_8 | ||
} | ||
kotlinOptions { | ||
jvmTarget = '1.8' | ||
} | ||
buildFeatures.viewBinding = true | ||
} | ||
|
||
dependencies { | ||
implementation 'androidx.core:core-ktx:1.3.2' | ||
implementation 'androidx.appcompat:appcompat:1.2.0' | ||
implementation 'com.google.android.material:material:1.3.0' | ||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' | ||
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.4' | ||
implementation 'androidx.navigation:navigation-ui-ktx:2.3.4' | ||
} |
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,23 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
xmlns:tools="http://schemas.android.com/tools" | ||
package="com.husaynhakeem.rendereffectsample"> | ||
|
||
<application | ||
android:allowBackup="true" | ||
android:label="@string/app_name" | ||
android:supportsRtl="true" | ||
android:theme="@style/Theme.RenderEffectSample" | ||
tools:ignore="AllowBackup,MissingApplicationIcon"> | ||
<activity | ||
android:name=".MainActivity" | ||
android:exported="true"> | ||
<intent-filter> | ||
<action android:name="android.intent.action.MAIN" /> | ||
|
||
<category android:name="android.intent.category.LAUNCHER" /> | ||
</intent-filter> | ||
</activity> | ||
</application> | ||
|
||
</manifest> |
71 changes: 71 additions & 0 deletions
71
...rEffectSample/app/src/main/java/com/husaynhakeem/rendereffectsample/BlurEffectFragment.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,71 @@ | ||
package com.husaynhakeem.rendereffectsample | ||
|
||
import android.graphics.RenderEffect | ||
import android.graphics.Shader | ||
import android.os.Bundle | ||
import android.view.Gravity | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.fragment.app.Fragment | ||
import com.google.android.material.chip.Chip | ||
import com.google.android.material.slider.Slider | ||
import com.husaynhakeem.rendereffectsample.databinding.FragmentBlurEffectBinding | ||
|
||
class BlurEffectFragment : Fragment() { | ||
|
||
private lateinit var binding: FragmentBlurEffectBinding | ||
private var radiusX: Float = 0F | ||
private var radiusY: Float = 0F | ||
private var tileMode: Shader.TileMode = Shader.TileMode.MIRROR | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, | ||
container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View { | ||
binding = FragmentBlurEffectBinding.inflate(layoutInflater) | ||
return binding.root | ||
} | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
// Set initial values | ||
binding.radiusXSlider.value = radiusX | ||
binding.radiusYSlider.value = radiusY | ||
|
||
binding.radiusXSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> | ||
this.radiusX = value | ||
updateEffect() | ||
}) | ||
|
||
binding.radiusYSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> | ||
this.radiusY = value | ||
updateEffect() | ||
}) | ||
|
||
// Add all tile modes as chips in the ChipGroup | ||
Shader.TileMode.values() | ||
.forEach { tileMode -> | ||
binding.tileModes.addView(tileMode.toChip(this.tileMode == tileMode)) | ||
} | ||
} | ||
|
||
private fun Shader.TileMode.toChip(isChecked: Boolean): Chip { | ||
val chip = layoutInflater.inflate(R.layout.choice_chip, null, false) as Chip | ||
chip.text = this.name | ||
chip.gravity = Gravity.CENTER | ||
chip.isChecked = isChecked | ||
chip.setOnClickListener { | ||
this@BlurEffectFragment.tileMode = this | ||
updateEffect() | ||
} | ||
return chip | ||
} | ||
|
||
private fun updateEffect() { | ||
val effect = RenderEffect.createBlurEffect(radiusX, radiusY, tileMode) | ||
binding.imageView.setRenderEffect(effect) | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
...Sample/app/src/main/java/com/husaynhakeem/rendereffectsample/ColorFilterEffectFragment.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,85 @@ | ||
package com.husaynhakeem.rendereffectsample | ||
|
||
import android.graphics.BlendMode | ||
import android.graphics.BlendModeColorFilter | ||
import android.graphics.Color | ||
import android.graphics.RenderEffect | ||
import android.os.Bundle | ||
import android.view.Gravity | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.fragment.app.Fragment | ||
import com.google.android.material.chip.Chip | ||
import com.google.android.material.slider.Slider | ||
import com.husaynhakeem.rendereffectsample.databinding.FragmentColorFilterEffectBinding | ||
|
||
class ColorFilterEffectFragment : Fragment() { | ||
|
||
private lateinit var binding: FragmentColorFilterEffectBinding | ||
private var red: Int = 127 | ||
private var green: Int = 127 | ||
private var blue: Int = 127 | ||
private var blendMode: BlendMode = BlendMode.COLOR | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, | ||
container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View { | ||
binding = FragmentColorFilterEffectBinding.inflate(layoutInflater) | ||
return binding.root | ||
} | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
// Set sliders initial values | ||
binding.redSlider.value = red.toFloat() | ||
binding.greenSlider.value = green.toFloat() | ||
binding.blueSlider.value = blue.toFloat() | ||
updateRenderEffect() | ||
|
||
// Set up listener on color sliders | ||
binding.redSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> | ||
this.red = value.toInt() | ||
updateRenderEffect() | ||
}) | ||
binding.greenSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> | ||
this.green = value.toInt() | ||
updateRenderEffect() | ||
}) | ||
binding.blueSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> | ||
this.blue = value.toInt() | ||
updateRenderEffect() | ||
}) | ||
|
||
// Add all blend modes as chips in the ChipGroup | ||
BlendMode.values() | ||
.reversed() | ||
.forEach { blendMode -> | ||
binding.blendModes.addView(blendMode.toChip(this.blendMode == blendMode)) | ||
} | ||
} | ||
|
||
private fun BlendMode.toChip(isChecked: Boolean): Chip { | ||
val chip = layoutInflater.inflate(R.layout.choice_chip, null, false) as Chip | ||
chip.text = this.name | ||
chip.gravity = Gravity.CENTER | ||
chip.isChecked = isChecked | ||
chip.setOnClickListener { | ||
this@ColorFilterEffectFragment.blendMode = this | ||
updateRenderEffect() | ||
} | ||
return chip | ||
} | ||
|
||
private fun updateRenderEffect() { | ||
val color = Color.rgb(red, green, blue) | ||
binding.color.setBackgroundColor(color) | ||
|
||
val colorFilter = BlendModeColorFilter(color, blendMode) | ||
val effect = RenderEffect.createColorFilterEffect(colorFilter) | ||
binding.imageView.setRenderEffect(effect) | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
RenderEffectSample/app/src/main/java/com/husaynhakeem/rendereffectsample/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,12 @@ | ||
package com.husaynhakeem.rendereffectsample | ||
|
||
import android.os.Bundle | ||
import androidx.appcompat.app.AppCompatActivity | ||
|
||
class MainActivity : AppCompatActivity() { | ||
|
||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
setContentView(R.layout.main_activity) | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
RenderEffectSample/app/src/main/java/com/husaynhakeem/rendereffectsample/MainFragment.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.rendereffectsample | ||
|
||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import androidx.fragment.app.Fragment | ||
import androidx.navigation.fragment.findNavController | ||
import com.husaynhakeem.rendereffectsample.databinding.FragmentMainBinding | ||
|
||
class MainFragment : Fragment() { | ||
|
||
private lateinit var binding: FragmentMainBinding | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, | ||
container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View { | ||
binding = FragmentMainBinding.inflate(inflater, container, false) | ||
return binding.root | ||
} | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
binding.blurEffectButton.setOnClickListener { | ||
findNavController().navigate(R.id.action_mainFragment_to_blurEffectFragment) | ||
} | ||
binding.colorFilterEffectButton.setOnClickListener { | ||
findNavController().navigate(R.id.action_mainFragment_to_colorFilterEffectFragment) | ||
} | ||
binding.offsetEffectButton.setOnClickListener { | ||
findNavController().navigate(R.id.action_mainFragment_to_offsetEffectFragment) | ||
} | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
...ffectSample/app/src/main/java/com/husaynhakeem/rendereffectsample/OffsetEffectFragment.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,64 @@ | ||
package com.husaynhakeem.rendereffectsample | ||
|
||
import android.graphics.RenderEffect | ||
import android.os.Bundle | ||
import android.view.LayoutInflater | ||
import android.view.View | ||
import android.view.ViewGroup | ||
import android.view.ViewTreeObserver | ||
import androidx.fragment.app.Fragment | ||
import com.google.android.material.slider.Slider | ||
import com.husaynhakeem.rendereffectsample.databinding.FragmentOffsetEffectBinding | ||
|
||
class OffsetEffectFragment : Fragment() { | ||
|
||
private lateinit var binding: FragmentOffsetEffectBinding | ||
private var offsetX: Float = 0F | ||
private var offsetY: Float = 0F | ||
|
||
override fun onCreateView( | ||
inflater: LayoutInflater, | ||
container: ViewGroup?, | ||
savedInstanceState: Bundle? | ||
): View { | ||
binding = FragmentOffsetEffectBinding.inflate(inflater, container, false) | ||
return binding.root | ||
} | ||
|
||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
|
||
// Set up sliders with initial values | ||
binding.offsetXSlider.value = offsetX | ||
binding.offsetYSlider.value = offsetY | ||
|
||
// Set up to values for the sliders | ||
binding.imageView | ||
.viewTreeObserver | ||
.addOnGlobalLayoutListener(object : | ||
ViewTreeObserver.OnGlobalLayoutListener { | ||
override fun onGlobalLayout() { | ||
binding.offsetXSlider.valueFrom = -binding.imageView.width.toFloat() | ||
binding.offsetXSlider.valueTo = binding.imageView.width.toFloat() | ||
binding.offsetYSlider.valueFrom = -binding.imageView.height.toFloat() | ||
binding.offsetYSlider.valueTo = binding.imageView.height.toFloat() | ||
binding.imageView.viewTreeObserver.removeOnGlobalLayoutListener(this) | ||
} | ||
}) | ||
|
||
// Set up listeners on sliders | ||
binding.offsetXSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> | ||
offsetX = value | ||
updateEffect() | ||
}) | ||
binding.offsetYSlider.addOnChangeListener(Slider.OnChangeListener { _, value, _ -> | ||
offsetY = value | ||
updateEffect() | ||
}) | ||
} | ||
|
||
private fun updateEffect() { | ||
val effect = RenderEffect.createOffsetEffect(offsetX, offsetY) | ||
binding.imageView.setRenderEffect(effect) | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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,5 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android" | ||
style="@style/Widget.MaterialComponents.Chip.Choice" | ||
android:layout_width="wrap_content" | ||
android:layout_height="wrap_content" /> |
Oops, something went wrong.