View.kt revision 57c7350b439c472eb39f55fea4511b530d23f982
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17@file:Suppress("NOTHING_TO_INLINE") // Aliases to other public API. 18 19package androidx.core.view 20 21import android.graphics.Bitmap 22import android.view.View 23import android.view.ViewGroup 24import android.view.ViewTreeObserver 25import android.view.accessibility.AccessibilityEvent 26import androidx.annotation.Px 27import androidx.annotation.RequiresApi 28import androidx.annotation.StringRes 29import androidx.core.graphics.applyCanvas 30 31/** 32 * Performs the given action when this view is next laid out. 33 * 34 * @see doOnLayout 35 */ 36inline fun View.doOnNextLayout(crossinline action: (view: View) -> Unit) { 37 addOnLayoutChangeListener(object : View.OnLayoutChangeListener { 38 override fun onLayoutChange( 39 view: View, 40 left: Int, 41 top: Int, 42 right: Int, 43 bottom: Int, 44 oldLeft: Int, 45 oldTop: Int, 46 oldRight: Int, 47 oldBottom: Int 48 ) { 49 view.removeOnLayoutChangeListener(this) 50 action(view) 51 } 52 }) 53} 54 55/** 56 * Performs the given action when this view is laid out. If the view has been laid out and it 57 * has not requested a layout, the action will be performed straight away, otherwise the 58 * action will be performed after the view is next laid out. 59 * 60 * @see doOnNextLayout 61 */ 62inline fun View.doOnLayout(crossinline action: (view: View) -> Unit) { 63 if (ViewCompat.isLaidOut(this) && !isLayoutRequested) { 64 action(this) 65 } else { 66 doOnNextLayout { 67 action(it) 68 } 69 } 70} 71 72/** 73 * Performs the given action when the view tree is about to be drawn. 74 */ 75inline fun View.doOnPreDraw(crossinline action: (view: View) -> Unit) { 76 val vto = viewTreeObserver 77 vto.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { 78 override fun onPreDraw(): Boolean { 79 action(this@doOnPreDraw) 80 when { 81 vto.isAlive -> vto.removeOnPreDrawListener(this) 82 else -> viewTreeObserver.removeOnPreDrawListener(this) 83 } 84 return true 85 } 86 }) 87} 88 89/** 90 * Sends [AccessibilityEvent] of type [AccessibilityEvent.TYPE_ANNOUNCEMENT]. 91 * 92 * @see View.announceForAccessibility 93 */ 94@RequiresApi(16) 95inline fun View.announceForAccessibility(@StringRes resource: Int) { 96 val announcement = resources.getString(resource) 97 announceForAccessibility(announcement) 98} 99 100/** 101 * Updates this view's relative padding. This version of the method allows using named parameters 102 * to just set one or more axes. 103 * 104 * @see View.setPaddingRelative 105 */ 106@RequiresApi(17) 107inline fun View.updatePaddingRelative( 108 @Px start: Int = paddingStart, 109 @Px top: Int = paddingTop, 110 @Px end: Int = paddingEnd, 111 @Px bottom: Int = paddingBottom 112) { 113 setPaddingRelative(start, top, end, bottom) 114} 115 116/** 117 * Updates this view's padding. This version of the method allows using named parameters 118 * to just set one or more axes. 119 * 120 * @see View.setPadding 121 */ 122inline fun View.updatePadding( 123 @Px left: Int = paddingLeft, 124 @Px top: Int = paddingTop, 125 @Px right: Int = paddingRight, 126 @Px bottom: Int = paddingBottom 127) { 128 setPadding(left, top, right, bottom) 129} 130 131/** 132 * Sets the view's padding. This version of the method sets all axes to the provided size. 133 * 134 * @see View.setPadding 135 */ 136inline fun View.setPadding(@Px size: Int) { 137 setPadding(size, size, size, size) 138} 139 140/** 141 * Version of [View.postDelayed] which re-orders the parameters, allowing the action to be placed 142 * outside of parentheses. 143 * 144 * ``` 145 * view.postDelayed(200) { 146 * doSomething() 147 * } 148 * ``` 149 * 150 * @return the created Runnable 151 */ 152inline fun View.postDelayed(delayInMillis: Long, crossinline action: () -> Unit): Runnable { 153 val runnable = Runnable { action() } 154 postDelayed(runnable, delayInMillis) 155 return runnable 156} 157 158/** 159 * Version of [View.postOnAnimationDelayed] which re-orders the parameters, allowing the action 160 * to be placed outside of parentheses. 161 * 162 * ``` 163 * view.postOnAnimationDelayed(16) { 164 * doSomething() 165 * } 166 * ``` 167 * 168 * @return the created Runnable 169 */ 170@RequiresApi(16) 171inline fun View.postOnAnimationDelayed( 172 delayInMillis: Long, 173 crossinline action: () -> Unit 174): Runnable { 175 val runnable = Runnable { action() } 176 postOnAnimationDelayed(runnable, delayInMillis) 177 return runnable 178} 179 180/** 181 * Return a [Bitmap] representation of this [View]. 182 * 183 * The resulting bitmap will be the same width and height as this view's current layout 184 * dimensions. This does not take into account any transformations such as scale or translation. 185 * 186 * Note, this will use the software rendering pipeline to draw the view to the bitmap. This may 187 * result with different drawing to what is rendered on a hardware accelerated canvas (such as 188 * the device screen). 189 * 190 * If this view has not been laid out this method will throw a [IllegalStateException]. 191 * 192 * @param config Bitmap config of the desired bitmap. Defaults to [Bitmap.Config.ARGB_8888]. 193 */ 194fun View.toBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap { 195 if (!ViewCompat.isLaidOut(this)) { 196 throw IllegalStateException("View needs to be laid out before calling toBitmap()") 197 } 198 return Bitmap.createBitmap(width, height, config).applyCanvas { 199 translate(-scrollX.toFloat(), -scrollY.toFloat()) 200 draw(this) 201 } 202} 203 204/** 205 * Returns true when this view's visibility is [View.VISIBLE], false otherwise. 206 * 207 * ``` 208 * if (view.isVisible) { 209 * // Behavior... 210 * } 211 * ``` 212 * 213 * Setting this property to true sets the visibility to [View.VISIBLE], false to [View.GONE]. 214 * 215 * ``` 216 * view.isVisible = true 217 * ``` 218 */ 219inline var View.isVisible: Boolean 220 get() = visibility == View.VISIBLE 221 set(value) { 222 visibility = if (value) View.VISIBLE else View.GONE 223 } 224 225/** 226 * Returns true when this view's visibility is [View.INVISIBLE], false otherwise. 227 * 228 * ``` 229 * if (view.isInvisible) { 230 * // Behavior... 231 * } 232 * ``` 233 * 234 * Setting this property to true sets the visibility to [View.INVISIBLE], false to [View.VISIBLE]. 235 * 236 * ``` 237 * view.isInvisible = true 238 * ``` 239 */ 240inline var View.isInvisible: Boolean 241 get() = visibility == View.INVISIBLE 242 set(value) { 243 visibility = if (value) View.INVISIBLE else View.VISIBLE 244 } 245 246/** 247 * Returns true when this view's visibility is [View.GONE], false otherwise. 248 * 249 * ``` 250 * if (view.isGone) { 251 * // Behavior... 252 * } 253 * ``` 254 * 255 * Setting this property to true sets the visibility to [View.GONE], false to [View.VISIBLE]. 256 * 257 * ``` 258 * view.isGone = true 259 * ``` 260 */ 261inline var View.isGone: Boolean 262 get() = visibility == View.GONE 263 set(value) { 264 visibility = if (value) View.GONE else View.VISIBLE 265 } 266 267/** 268 * Executes [block] with the View's layoutParams and reassigns the layoutParams with the 269 * updated version. 270 * 271 * @see View.getLayoutParams 272 * @see View.setLayoutParams 273 **/ 274inline fun View.updateLayoutParams(block: ViewGroup.LayoutParams.() -> Unit) { 275 updateLayoutParams<ViewGroup.LayoutParams>(block) 276} 277 278/** 279 * Executes [block] with a typed version of the View's layoutParams and reassigns the 280 * layoutParams with the updated version. 281 * 282 * @see View.getLayoutParams 283 * @see View.setLayoutParams 284 **/ 285@JvmName("updateLayoutParamsTyped") 286inline fun <reified T : ViewGroup.LayoutParams> View.updateLayoutParams(block: T.() -> Unit) { 287 val params = layoutParams as T 288 block(params) 289 layoutParams = params 290} 291