1 /* 2 * Copyright (C) 2016 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 package com.android.car.apps.common; 17 18 import android.content.Context; 19 import android.graphics.Bitmap; 20 import android.graphics.Color; 21 import android.util.AttributeSet; 22 import android.view.View; 23 import android.view.ViewGroup; 24 import android.view.animation.Animation; 25 import android.view.animation.AnimationUtils; 26 import android.view.animation.DecelerateInterpolator; 27 import android.widget.FrameLayout; 28 import android.widget.ImageView; 29 30 import java.util.Objects; 31 32 /** 33 * A view where updating the image will show certain animations. Current animations include fading 34 * in and scaling down the new image. 35 */ 36 public class CrossfadeImageView extends FrameLayout { 37 private final CropAlignedImageView mImageView1; 38 private final CropAlignedImageView mImageView2; 39 40 private CropAlignedImageView mActiveImageView; 41 private CropAlignedImageView mInactiveImageView; 42 43 private Bitmap mCurrentBitmap = null; 44 private Integer mCurrentColor = null; 45 private Animation mImageInAnimation; 46 private Animation mImageOutAnimation; 47 CrossfadeImageView(Context context)48 public CrossfadeImageView(Context context) { 49 this(context, null); 50 } 51 CrossfadeImageView(Context context, AttributeSet attrs)52 public CrossfadeImageView(Context context, AttributeSet attrs) { 53 this(context, attrs, 0); 54 } 55 CrossfadeImageView(Context context, AttributeSet attrs, int defStyleAttr)56 public CrossfadeImageView(Context context, AttributeSet attrs, int defStyleAttr) { 57 this(context, attrs, defStyleAttr, 0); 58 } 59 CrossfadeImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)60 public CrossfadeImageView(Context context, AttributeSet attrs, 61 int defStyleAttr, int defStyleRes) { 62 super(context, attrs, defStyleAttr, defStyleRes); 63 LayoutParams lp = new LayoutParams( 64 ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 65 ImageView imageViewBackground = new ImageView(context, attrs, defStyleAttr, defStyleRes); 66 imageViewBackground.setLayoutParams(lp); 67 imageViewBackground.setBackgroundColor(Color.BLACK); 68 addView(imageViewBackground); 69 mImageView1 = new CropAlignedImageView(context, attrs, defStyleAttr, defStyleRes); 70 mImageView1.setLayoutParams(lp); 71 addView(mImageView1); 72 mImageView2 = new CropAlignedImageView(context, attrs, defStyleAttr, defStyleRes); 73 mImageView2.setLayoutParams(lp); 74 addView(mImageView2); 75 76 mActiveImageView = mImageView1; 77 mInactiveImageView = mImageView2; 78 79 mImageInAnimation = AnimationUtils.loadAnimation(context, R.anim.image_in); 80 mImageInAnimation.setInterpolator(new DecelerateInterpolator()); 81 mImageOutAnimation = AnimationUtils.loadAnimation(context, R.anim.image_out); 82 } 83 84 /** 85 * Sets the image to show on this view 86 * 87 * @param bitmap image to show 88 * @param showAnimation whether the transition should be animated or not. 89 */ setImageBitmap(Bitmap bitmap, boolean showAnimation)90 public void setImageBitmap(Bitmap bitmap, boolean showAnimation) { 91 if (Objects.equals(mCurrentBitmap, bitmap)) { 92 return; 93 } 94 95 mCurrentBitmap = bitmap; 96 mCurrentColor = null; 97 mInactiveImageView.setImageBitmap(bitmap); 98 if (showAnimation) { 99 animateViews(); 100 } else { 101 mActiveImageView.setImageBitmap(bitmap); 102 } 103 } 104 105 /** 106 * Sets a plain color as background, with an animated transition 107 */ 108 @Override setBackgroundColor(int color)109 public void setBackgroundColor(int color) { 110 if (mCurrentColor != null && mCurrentColor == color) { 111 return; 112 } 113 mInactiveImageView.setImageBitmap(null); 114 mCurrentBitmap = null; 115 mCurrentColor = color; 116 mInactiveImageView.setBackgroundColor(color); 117 animateViews(); 118 } 119 120 private final Animation.AnimationListener mAnimationListener = 121 new Animation.AnimationListener() { 122 @Override 123 public void onAnimationEnd(Animation animation) { 124 if (mInactiveImageView != null) { 125 mInactiveImageView.setVisibility(View.GONE); 126 } 127 } 128 129 @Override 130 public void onAnimationStart(Animation animation) { } 131 132 @Override 133 public void onAnimationRepeat(Animation animation) { } 134 }; 135 animateViews()136 private void animateViews() { 137 mInactiveImageView.setVisibility(View.VISIBLE); 138 mInactiveImageView.startAnimation(mImageInAnimation); 139 mInactiveImageView.bringToFront(); 140 mActiveImageView.startAnimation(mImageOutAnimation); 141 mImageOutAnimation.setAnimationListener(mAnimationListener); 142 if (mActiveImageView == mImageView1) { 143 mActiveImageView = mImageView2; 144 mInactiveImageView = mImageView1; 145 } else { 146 mActiveImageView = mImageView1; 147 mInactiveImageView = mImageView2; 148 } 149 } 150 151 /** 152 * Sets the additional image scale. See {@link 153 * com.android.car.apps.common.CropAlignedImageView#setImageAdditionalScale(float)} 154 * for more details. 155 */ setImageAdditionalScale(float scale)156 public void setImageAdditionalScale(float scale) { 157 mImageView2.setImageAdditionalScale(scale); 158 mImageView1.setImageAdditionalScale(scale); 159 } 160 } 161