1 /* 2 * Copyright (C) 2018 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.launcher3.util; 17 18 import android.animation.ArgbEvaluator; 19 import android.animation.ObjectAnimator; 20 import android.animation.ValueAnimator; 21 import android.graphics.Color; 22 import android.graphics.drawable.ColorDrawable; 23 import android.graphics.drawable.Drawable; 24 import android.support.v4.graphics.ColorUtils; 25 import android.view.View; 26 import android.view.View.OnLayoutChangeListener; 27 import android.widget.AbsListView; 28 import android.widget.AbsListView.OnScrollListener; 29 import android.widget.AbsListView.RecyclerListener; 30 import android.widget.ListView; 31 32 import com.android.launcher3.R; 33 34 /** 35 * Utility class to scroll and highlight a list view item 36 */ 37 public class ListViewHighlighter implements OnScrollListener, RecyclerListener, 38 OnLayoutChangeListener { 39 40 private final ListView mListView; 41 private int mPosHighlight; 42 43 private boolean mColorAnimated = false; 44 45 public ListViewHighlighter(ListView listView, int posHighlight) { 46 mListView = listView; 47 mPosHighlight = posHighlight; 48 mListView.setOnScrollListener(this); 49 mListView.setRecyclerListener(this); 50 mListView.addOnLayoutChangeListener(this); 51 52 mListView.post(this::tryHighlight); 53 } 54 55 @Override 56 public void onLayoutChange(View v, int left, int top, int right, int bottom, 57 int oldLeft, int oldTop, int oldRight, int oldBottom) { 58 mListView.post(this::tryHighlight); 59 } 60 61 private void tryHighlight() { 62 if (mPosHighlight < 0 || mListView.getChildCount() == 0) { 63 return; 64 } 65 if (!highlightIfVisible(mListView.getFirstVisiblePosition(), 66 mListView.getLastVisiblePosition())) { 67 mListView.smoothScrollToPosition(mPosHighlight); 68 } 69 } 70 71 @Override 72 public void onScrollStateChanged(AbsListView absListView, int i) { } 73 74 @Override 75 public void onScroll(AbsListView view, int firstVisibleItem, 76 int visibleItemCount, int totalItemCount) { 77 highlightIfVisible(firstVisibleItem, firstVisibleItem + visibleItemCount); 78 } 79 80 private boolean highlightIfVisible(int start, int end) { 81 if (mPosHighlight < 0 || mListView.getChildCount() == 0) { 82 return false; 83 } 84 if (start > mPosHighlight || mPosHighlight > end) { 85 return false; 86 } 87 highlightView(mListView.getChildAt(mPosHighlight - start)); 88 89 // finish highlight 90 mListView.setOnScrollListener(null); 91 mListView.removeOnLayoutChangeListener(this); 92 93 mPosHighlight = -1; 94 return true; 95 } 96 97 @Override 98 public void onMovedToScrapHeap(View view) { 99 unhighlightView(view); 100 } 101 102 private void highlightView(View view) { 103 if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) { 104 // already highlighted 105 } else { 106 view.setTag(R.id.view_highlighted, true); 107 view.setTag(R.id.view_unhighlight_background, view.getBackground()); 108 view.setBackground(getHighlightBackground()); 109 view.postDelayed(() -> { 110 unhighlightView(view); 111 }, 15000L); 112 } 113 } 114 115 private void unhighlightView(View view) { 116 if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) { 117 Object background = view.getTag(R.id.view_unhighlight_background); 118 if (background instanceof Drawable) { 119 view.setBackground((Drawable) background); 120 } 121 view.setTag(R.id.view_unhighlight_background, null); 122 view.setTag(R.id.view_highlighted, false); 123 } 124 } 125 126 private ColorDrawable getHighlightBackground() { 127 int color = ColorUtils.setAlphaComponent(Themes.getColorAccent(mListView.getContext()), 26); 128 if (mColorAnimated) { 129 return new ColorDrawable(color); 130 } 131 mColorAnimated = true; 132 ColorDrawable bg = new ColorDrawable(Color.WHITE); 133 ObjectAnimator anim = ObjectAnimator.ofInt(bg, "color", Color.WHITE, color); 134 anim.setEvaluator(new ArgbEvaluator()); 135 anim.setDuration(200L); 136 anim.setRepeatMode(ValueAnimator.REVERSE); 137 anim.setRepeatCount(4); 138 anim.start(); 139 return bg; 140 } 141 } 142