diff options
Diffstat (limited to 'src/com/android/launcher3/AppDrawerScrubber.java')
-rw-r--r-- | src/com/android/launcher3/AppDrawerScrubber.java | 239 |
1 files changed, 192 insertions, 47 deletions
diff --git a/src/com/android/launcher3/AppDrawerScrubber.java b/src/com/android/launcher3/AppDrawerScrubber.java index 0ace60da8..1670934d6 100644 --- a/src/com/android/launcher3/AppDrawerScrubber.java +++ b/src/com/android/launcher3/AppDrawerScrubber.java @@ -19,26 +19,30 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; -public class AppDrawerScrubber extends LinearLayout { - - private final int SCRUBBER_INDICATOR_DISPLAY_DURATION = 200; - private final float SCRUBBER_INDICATOR_DISPLAY_TRANSLATIONY = 20f; +import java.util.ArrayList; +public class AppDrawerScrubber extends LinearLayout { private AppDrawerListAdapter mAdapter; private RecyclerView mListView; private TextView mScrubberIndicator; private SeekBar mSeekBar; - private String[] mSections; + private AutoExpandTextView mScrubberText; + private SectionContainer mSectionContainer; private LinearLayoutManager mLayoutManager; + private ScrubberAnimationState mScrubberAnimationState; public AppDrawerScrubber(Context context, AttributeSet attrs) { super(context, attrs); @@ -50,9 +54,85 @@ public class AppDrawerScrubber extends LinearLayout { init(context); } + /** + * Simple container class that tries to abstract out the knowledge of complex sections vs + * simple string sections + */ + private static class SectionContainer { + private ArrayList<AppDrawerScrubberSections> mSections; + private String[] mHeaders; + + public SectionContainer(String[] headers) { + mSections = AppDrawerScrubberSections.createSections(headers); + mHeaders = headers; + } + + public int size() { + return showLetters() ? mSections.size() : mHeaders.length; + } + + public String getHeader(int idx) { + return showLetters() ? mSections.get(idx).getText() : mHeaders[idx]; + } + + /** + * Because the list section headers is not necessarily the same size as the scrubber + * letters, we need to map from the larger list to the smaller list. + * In the case that curIdx is not highlighted, it will use the directional index to + * determine the adapter index + * @return the mHeaders index (aka the underlying adapter index). + */ + public int getAdapterIndex(int prevIdx, int curIdx) { + if (!showLetters()) { + return curIdx; + } + + // because we have some unhighlighted letters, we need to first get the directional + // index before getting the adapter index + return mSections.get(getDirectionalIndex(prevIdx, curIdx)).getAdapterIndex(); + } + + /** + * Given the direction the user is scrolling in, return the closest index which is a + * highlighted index + */ + public int getDirectionalIndex(int prevIdx, int curIdx) { + if (!showLetters() || mSections.get(curIdx).getHighlight()) { + return curIdx; + } + + if (prevIdx < curIdx) { + return mSections.get(curIdx).getNextIndex(); + } else { + return mSections.get(curIdx).getPreviousIndex(); + } + } + + /** + * @return true if the scrubber is showing characters as opposed to a line + */ + public boolean showLetters() { + return mSections != null; + } + + /** + * Initializes the scrubber text with the proper characters + */ + public void initializeScrubberText(AutoExpandTextView scrubberText) { + scrubberText.setSections(AppDrawerScrubberSections.getHighlightText(mSections)); + } + } + public void updateSections() { - mSections = (String[]) mAdapter.getSections(); - mSeekBar.setMax(mSections.length - 1); + mSectionContainer = new SectionContainer((String[]) mAdapter.getSections()); + mSectionContainer.initializeScrubberText(mScrubberText); + mSeekBar.setMax(mSectionContainer.size() - 1); + + // show a white line if there are no letters, otherwise show transparent + Drawable d = mSectionContainer.showLetters() ? new ColorDrawable(Color.TRANSPARENT) + : getContext().getResources().getDrawable(R.drawable.seek_back); + ((ViewGroup)mSeekBar.getParent()).setBackground(d); + } public void setSource(RecyclerView listView) { @@ -68,63 +148,128 @@ public class AppDrawerScrubber extends LinearLayout { private boolean isReady() { return mListView != null && mAdapter != null && - mSections != null; + mSectionContainer != null; } private void init(Context context) { LayoutInflater.from(context).inflate(R.layout.scrub_layout, this); + mScrubberAnimationState = new ScrubberAnimationState(); mSeekBar = (SeekBar) findViewById(R.id.scrubber); + mScrubberText = (AutoExpandTextView) findViewById(R.id.scrubberText); + mSeekBar.setOnSeekBarChangeListener(mScrubberAnimationState); + } - mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar seekBar, final int progress, boolean fromUser) { - if (!isReady()) { - return; - } - resetScrubber(); + /** + * Handles the animations of the scrubber indicator + */ + private class ScrubberAnimationState implements SeekBar.OnSeekBarChangeListener { + private static final long SCRUBBER_DISPLAY_DURATION = 150; + private static final float SCRUBBER_SCALE_START = 0f; + private static final float SCRUBBER_SCALE_END = 1f; + private static final float SCRUBBER_ALPHA_START = 0f; + private static final float SCRUBBER_ALPHA_END = 1f; + + private boolean mTouchingTrack = false; + private boolean mAnimatingIn = false; + private int mLastIndex = -1; - String section = String.valueOf(mSections[progress]); + private void touchTrack(boolean touching) { + mTouchingTrack = touching; - if (mScrubberIndicator != null) { - float translateX = (progress * seekBar.getWidth()) / mSections.length; - translateX -= (mScrubberIndicator.getWidth() / 6); // offset for alignment - mScrubberIndicator.setTranslationX(translateX); - mScrubberIndicator.setText(section); + if (mScrubberIndicator != null) { + if (mTouchingTrack) { + animateIn(); + } else if (!mAnimatingIn) { // finish animating in before animating out + animateOut(); } - mLayoutManager.smoothScrollToPosition(mListView, null, - mAdapter.getPositionForSection(progress)); + mAdapter.setDragging(mTouchingTrack); } + } - @Override - public void onStartTrackingTouch(SeekBar seekBar) { - resetScrubber(); - if (mScrubberIndicator != null) { - mScrubberIndicator.setAlpha(1f); - mScrubberIndicator.setVisibility(View.VISIBLE); - } - } + private void animateIn() { + // start from a scratch position when animating in + mScrubberIndicator.animate().cancel(); + mScrubberIndicator.setPivotX(mScrubberIndicator.getMeasuredWidth() / 2); + mScrubberIndicator.setPivotY(mScrubberIndicator.getMeasuredHeight() * 0.8f); + mScrubberIndicator.setAlpha(SCRUBBER_ALPHA_START); + mScrubberIndicator.setScaleX(SCRUBBER_SCALE_START); + mScrubberIndicator.setScaleY(SCRUBBER_SCALE_START); + mScrubberIndicator.setVisibility(View.VISIBLE); + mAnimatingIn = true; - @Override - public void onStopTrackingTouch(SeekBar seekBar) { - resetScrubber(); - if (mScrubberIndicator != null) { - mScrubberIndicator.animate().alpha(0f).translationYBy(20f) - .setDuration(200).setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mScrubberIndicator.setVisibility(View.INVISIBLE); + mScrubberIndicator.animate() + .alpha(SCRUBBER_ALPHA_END) + .scaleX(SCRUBBER_SCALE_END) + .scaleY(SCRUBBER_SCALE_END) + .setDuration(SCRUBBER_DISPLAY_DURATION) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mAnimatingIn = false; + // if the user has stopped touching the seekbar, animate back out + if (!mTouchingTrack) { + animateOut(); } - }); - } + } + }) + .start(); + } + + private void animateOut() { + mScrubberIndicator.animate() + .alpha(SCRUBBER_ALPHA_START) + .scaleX(SCRUBBER_SCALE_START) + .scaleY(SCRUBBER_SCALE_START) + .setDuration(SCRUBBER_DISPLAY_DURATION) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mScrubberIndicator.setVisibility(View.INVISIBLE); + } + }); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int index, boolean fromUser) { + if (!isReady()) { + return; } - private void resetScrubber() { - if (mScrubberIndicator != null) { - mScrubberIndicator.animate().cancel(); - mScrubberIndicator.setTranslationY(0f); + if (mScrubberIndicator != null) { + // get the index based on the direction the user is scrolling + int directionalIndex = mSectionContainer.getDirectionalIndex(mLastIndex, index); + String sectionText = mSectionContainer.getHeader(directionalIndex); + + float translateX = (index * seekBar.getWidth()) / (float)mSectionContainer.size(); + // if we are showing letters, grab the position based on the text view + if (mSectionContainer.showLetters()) { + translateX = mScrubberText.getPositionOfSection(index); } + + // center the x position + translateX -= mScrubberIndicator.getMeasuredWidth() / 2; + + mScrubberIndicator.setTranslationX(translateX); + mScrubberIndicator.setText(sectionText); } - }); + + // get the index of the underlying list + int adapterIndex = mSectionContainer.getAdapterIndex(mLastIndex, index); + mLayoutManager.smoothScrollToPosition(mListView, null, + mAdapter.getPositionForSection(adapterIndex)); + + mLastIndex = index; + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + touchTrack(true); + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + touchTrack(false); + } } }
\ No newline at end of file |