Merge "Updating sticky headers." into ub-launcher3-burnaby
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
android:paddingBottom="12dp"
|
||||
android:contentDescription="@string/all_apps_button_label"
|
||||
android:src="@drawable/ic_arrow_back_grey" />
|
||||
<EditText
|
||||
<com.android.launcher3.AppsContainerSearchEditTextView
|
||||
android:id="@+id/app_search_box"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@@ -59,6 +59,7 @@
|
||||
android:textColor="#4c4c4c"
|
||||
android:textColorHint="#9c9c9c"
|
||||
android:imeOptions="actionDone|flagNoExtractUi"
|
||||
android:focusableInTouchMode="true"
|
||||
android:background="@android:color/transparent" />
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
|
||||
@@ -104,28 +104,38 @@ public class AlphabeticalAppsList {
|
||||
public int position;
|
||||
// Whether or not the item at this adapter position is a section or not
|
||||
public boolean isSectionHeader;
|
||||
// The name of this section, or the section that this app is contained in
|
||||
// The name of this section, or the section section name of the app. Note that if this
|
||||
// app was merged into another section, then this may be a different name than the
|
||||
// sectionInfo's sectionName
|
||||
public String sectionName;
|
||||
// The section to which this app belongs
|
||||
public SectionInfo sectionInfo;
|
||||
// The index of this app in the section
|
||||
public int sectionAppIndex;
|
||||
// The associated AppInfo, or null if this adapter item is a section
|
||||
public AppInfo appInfo;
|
||||
// The index of this app (not including sections), or -1 if this adapter item is a section
|
||||
public int appIndex;
|
||||
|
||||
public static AdapterItem asSection(int pos, String name) {
|
||||
public static AdapterItem asSection(int pos, SectionInfo section) {
|
||||
AdapterItem item = new AdapterItem();
|
||||
item.position = pos;
|
||||
item.isSectionHeader = true;
|
||||
item.sectionName = name;
|
||||
item.sectionInfo = section;
|
||||
item.sectionName = section.sectionName;
|
||||
item.appInfo = null;
|
||||
item.appIndex = -1;
|
||||
return item;
|
||||
}
|
||||
|
||||
public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo, int appIndex) {
|
||||
public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
|
||||
int sectionAppIndex, AppInfo appInfo, int appIndex) {
|
||||
AdapterItem item = new AdapterItem();
|
||||
item.position = pos;
|
||||
item.isSectionHeader = false;
|
||||
item.sectionInfo = section;
|
||||
item.sectionName = sectionName;
|
||||
item.sectionAppIndex = sectionAppIndex;
|
||||
item.appInfo = appInfo;
|
||||
item.appIndex = appIndex;
|
||||
return item;
|
||||
@@ -140,7 +150,7 @@ public class AlphabeticalAppsList {
|
||||
}
|
||||
|
||||
// The maximum number of rows allowed in a merged section before we stop merging
|
||||
private static final int MAX_ROWS_IN_MERGED_SECTION = Integer.MAX_VALUE;
|
||||
private static final int MAX_ROWS_IN_MERGED_SECTION = 3;
|
||||
|
||||
private List<AppInfo> mApps = new ArrayList<>();
|
||||
private List<AppInfo> mFilteredApps = new ArrayList<>();
|
||||
@@ -314,6 +324,7 @@ public class AlphabeticalAppsList {
|
||||
SectionInfo lastSectionInfo = null;
|
||||
int position = 0;
|
||||
int appIndex = 0;
|
||||
int sectionAppIndex = 0;
|
||||
for (AppInfo info : mApps) {
|
||||
String sectionName = mIndexer.computeSectionName(info.title.toString().trim());
|
||||
|
||||
@@ -325,11 +336,12 @@ public class AlphabeticalAppsList {
|
||||
// Create a new section if necessary
|
||||
if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) {
|
||||
lastSectionInfo = new SectionInfo(sectionName);
|
||||
sectionAppIndex = 0;
|
||||
mSections.add(lastSectionInfo);
|
||||
|
||||
// Create a new section item, this item is used to break the flow of items in the
|
||||
// list
|
||||
AdapterItem sectionItem = AdapterItem.asSection(position++, sectionName);
|
||||
AdapterItem sectionItem = AdapterItem.asSection(position++, lastSectionInfo);
|
||||
if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) {
|
||||
lastSectionInfo.sectionItem = sectionItem;
|
||||
mSectionedFilteredApps.add(sectionItem);
|
||||
@@ -337,7 +349,8 @@ public class AlphabeticalAppsList {
|
||||
}
|
||||
|
||||
// Create an app item
|
||||
AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++);
|
||||
AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName,
|
||||
sectionAppIndex++, info, appIndex++);
|
||||
lastSectionInfo.numAppsInSection++;
|
||||
if (lastSectionInfo.firstAppItem == null) {
|
||||
lastSectionInfo.firstAppItem = appItem;
|
||||
@@ -361,25 +374,33 @@ public class AlphabeticalAppsList {
|
||||
// some limit, and also if there are no lessons to merge.
|
||||
while (0 < (sectionAppCount % mNumAppsPerRow) &&
|
||||
(sectionAppCount % mNumAppsPerRow) < minNumAppsPerRow &&
|
||||
(int) Math.ceil(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
|
||||
(sectionAppCount / mNumAppsPerRow) < MAX_ROWS_IN_MERGED_SECTION &&
|
||||
(i + 1) < mSections.size()) {
|
||||
SectionInfo nextSection = mSections.remove(i + 1);
|
||||
|
||||
// Merge the section names
|
||||
if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
|
||||
mergedSectionName += nextSection.sectionName;
|
||||
}
|
||||
// Remove the next section break
|
||||
mSectionedFilteredApps.remove(nextSection.sectionItem);
|
||||
int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
|
||||
if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
|
||||
// Update the section names for the two sections
|
||||
int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
|
||||
for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
|
||||
AdapterItem item = mSectionedFilteredApps.get(j);
|
||||
item.sectionName = mergedSectionName;
|
||||
item.sectionInfo = section;
|
||||
}
|
||||
}
|
||||
// Update the following adapter items of the removed section
|
||||
int pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
|
||||
// Point the section for these new apps to the merged section
|
||||
for (int j = pos + section.numAppsInSection; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
|
||||
AdapterItem item = mSectionedFilteredApps.get(j);
|
||||
item.sectionInfo = section;
|
||||
item.sectionAppIndex += section.numAppsInSection;
|
||||
}
|
||||
// Update the following adapter items of the removed section item
|
||||
pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
|
||||
for (int j = pos; j < mSectionedFilteredApps.size(); j++) {
|
||||
AdapterItem item = mSectionedFilteredApps.get(j);
|
||||
item.position--;
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.EditText;
|
||||
|
||||
|
||||
/**
|
||||
* The edit text for the search container
|
||||
*/
|
||||
public class AppsContainerSearchEditTextView extends EditText {
|
||||
|
||||
/**
|
||||
* Implemented by listeners of the back key.
|
||||
*/
|
||||
public interface OnBackKeyListener {
|
||||
public void onBackKey();
|
||||
}
|
||||
|
||||
private OnBackKeyListener mBackKeyListener;
|
||||
|
||||
public AppsContainerSearchEditTextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public AppsContainerSearchEditTextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public AppsContainerSearchEditTextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public void setOnBackKeyListener(OnBackKeyListener listener) {
|
||||
mBackKeyListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
// If this is a back key, propagate the key back to the listener
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
|
||||
if (mBackKeyListener != null) {
|
||||
mBackKeyListener.onBackKey();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return super.onKeyPreIme(keyCode, event);
|
||||
}
|
||||
}
|
||||
@@ -51,9 +51,11 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
|
||||
|
||||
private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
|
||||
private static final boolean DYNAMIC_HEADER_ELEVATION = false;
|
||||
private static final boolean DISMISS_SEARCH_ON_BACK = true;
|
||||
private static final float HEADER_ELEVATION_DP = 4;
|
||||
private static final int FADE_IN_DURATION = 175;
|
||||
private static final int FADE_OUT_DURATION = 125;
|
||||
private static final int FADE_OUT_DURATION = 100;
|
||||
private static final int SEARCH_TRANSLATION_X_DP = 18;
|
||||
|
||||
@Thunk Launcher mLauncher;
|
||||
@Thunk AlphabeticalAppsList mApps;
|
||||
@@ -67,7 +69,7 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
|
||||
private View mSearchBarContainerView;
|
||||
private View mSearchButtonView;
|
||||
private View mDismissSearchButtonView;
|
||||
private EditText mSearchBarEditView;
|
||||
private AppsContainerSearchEditTextView mSearchBarEditView;
|
||||
|
||||
private int mNumAppsPerRow;
|
||||
private Point mLastTouchDownPos = new Point(-1, -1);
|
||||
@@ -192,10 +194,19 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
|
||||
mSearchBarContainerView = findViewById(R.id.app_search_container);
|
||||
mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
|
||||
mDismissSearchButtonView.setOnClickListener(this);
|
||||
mSearchBarEditView = (EditText) findViewById(R.id.app_search_box);
|
||||
mSearchBarEditView = (AppsContainerSearchEditTextView) findViewById(R.id.app_search_box);
|
||||
if (mSearchBarEditView != null) {
|
||||
mSearchBarEditView.addTextChangedListener(this);
|
||||
mSearchBarEditView.setOnEditorActionListener(this);
|
||||
if (DISMISS_SEARCH_ON_BACK) {
|
||||
mSearchBarEditView.setOnBackKeyListener(
|
||||
new AppsContainerSearchEditTextView.OnBackKeyListener() {
|
||||
@Override
|
||||
public void onBackKey() {
|
||||
hideSearchField(true, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
mAppsRecyclerView = (AppsContainerRecyclerView) findViewById(R.id.apps_list_view);
|
||||
mAppsRecyclerView.setApps(mApps);
|
||||
@@ -563,9 +574,16 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
|
||||
*/
|
||||
private void showSearchField() {
|
||||
// Show the search bar and focus the search
|
||||
final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
|
||||
getContext().getResources().getDisplayMetrics());
|
||||
mSearchBarContainerView.setVisibility(View.VISIBLE);
|
||||
mSearchBarContainerView.setAlpha(0f);
|
||||
mSearchBarContainerView.animate().alpha(1f).setDuration(FADE_IN_DURATION).withLayer()
|
||||
mSearchBarContainerView.setTranslationX(translationX);
|
||||
mSearchBarContainerView.animate()
|
||||
.alpha(1f)
|
||||
.translationX(0)
|
||||
.setDuration(FADE_IN_DURATION)
|
||||
.withLayer()
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -574,38 +592,57 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
|
||||
InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
});
|
||||
mSearchButtonView.animate().alpha(0f).setDuration(FADE_OUT_DURATION).withLayer();
|
||||
mSearchButtonView.animate()
|
||||
.alpha(0f)
|
||||
.translationX(-translationX)
|
||||
.setDuration(FADE_OUT_DURATION)
|
||||
.withLayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the search field.
|
||||
*/
|
||||
private void hideSearchField(boolean animated, final boolean returnFocusToRecyclerView) {
|
||||
final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
|
||||
final int translationX = DynamicGrid.pxFromDp(SEARCH_TRANSLATION_X_DP,
|
||||
getContext().getResources().getDisplayMetrics());
|
||||
if (animated) {
|
||||
// Hide the search bar and focus the recycler view
|
||||
mSearchBarContainerView.animate().alpha(0f).setDuration(FADE_IN_DURATION).withLayer()
|
||||
mSearchBarContainerView.animate()
|
||||
.alpha(0f)
|
||||
.translationX(0)
|
||||
.setDuration(FADE_IN_DURATION)
|
||||
.withLayer()
|
||||
.withEndAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mSearchBarContainerView.setVisibility(View.INVISIBLE);
|
||||
mSearchBarEditView.setText("");
|
||||
if (resetTextField) {
|
||||
mSearchBarEditView.setText("");
|
||||
}
|
||||
mApps.setFilter(null);
|
||||
if (returnFocusToRecyclerView) {
|
||||
mAppsRecyclerView.requestFocus();
|
||||
}
|
||||
scrollToTop();
|
||||
}
|
||||
});
|
||||
mSearchButtonView.animate().alpha(1f).setDuration(FADE_OUT_DURATION).withLayer();
|
||||
mSearchButtonView.setTranslationX(-translationX);
|
||||
mSearchButtonView.animate()
|
||||
.alpha(1f)
|
||||
.translationX(0)
|
||||
.setDuration(FADE_OUT_DURATION)
|
||||
.withLayer();
|
||||
} else {
|
||||
mSearchBarContainerView.setVisibility(View.INVISIBLE);
|
||||
mSearchBarEditView.setText("");
|
||||
if (resetTextField) {
|
||||
mSearchBarEditView.setText("");
|
||||
}
|
||||
mApps.setFilter(null);
|
||||
mSearchButtonView.setAlpha(1f);
|
||||
mSearchButtonView.setTranslationX(0f);
|
||||
if (returnFocusToRecyclerView) {
|
||||
mAppsRecyclerView.requestFocus();
|
||||
}
|
||||
scrollToTop();
|
||||
}
|
||||
getInputMethodManager().hideSoftInputFromWindow(getWindowToken(), 0);
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -84,8 +84,10 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
|
||||
private static final boolean FADE_OUT_SECTIONS = false;
|
||||
|
||||
private HashMap<String, Point> mCachedSectionBounds = new HashMap<>();
|
||||
private HashMap<String, PointF> mCachedSectionBounds = new HashMap<>();
|
||||
private Rect mTmpBounds = new Rect();
|
||||
private String[] mTmpSections = new String[2];
|
||||
private PointF[] mTmpSectionBounds = new PointF[2];
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
@@ -94,75 +96,83 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
}
|
||||
|
||||
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
String lastSectionName = null;
|
||||
int appIndexInSection = 0;
|
||||
int childCount = parent.getChildCount();
|
||||
int lastSectionTop = 0;
|
||||
int lastSectionHeight = 0;
|
||||
for (int i = 0; i < parent.getChildCount(); i++) {
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View child = parent.getChildAt(i);
|
||||
ViewHolder holder = (ViewHolder) parent.getChildViewHolder(child);
|
||||
if (shouldDrawItemSection(holder, child, i, items)) {
|
||||
int cellTopOffset = (2 * child.getPaddingTop());
|
||||
// At this point, we only draw sections for each section break;
|
||||
int viewTopOffset = (2 * child.getPaddingTop());
|
||||
int pos = holder.getPosition();
|
||||
AlphabeticalAppsList.AdapterItem item = items.get(pos);
|
||||
if (!item.sectionName.equals(lastSectionName)) {
|
||||
lastSectionName = item.sectionName;
|
||||
AlphabeticalAppsList.SectionInfo sectionInfo = item.sectionInfo;
|
||||
|
||||
// Draw all the sections for this index
|
||||
String lastSectionName = item.sectionName;
|
||||
for (int j = item.sectionAppIndex; j < sectionInfo.numAppsInSection;j++, pos++) {
|
||||
AlphabeticalAppsList.AdapterItem nextItem = items.get(pos);
|
||||
if (nextItem.sectionInfo != sectionInfo) {
|
||||
break;
|
||||
}
|
||||
if (j > item.sectionAppIndex && nextItem.sectionName.equals(lastSectionName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the section code points
|
||||
String sectionBegin = null;
|
||||
String sectionEnd = null;
|
||||
int charOffset = 0;
|
||||
while (charOffset < item.sectionName.length()) {
|
||||
int codePoint = item.sectionName.codePointAt(charOffset);
|
||||
int codePointSize = Character.charCount(codePoint);
|
||||
if (charOffset == 0) {
|
||||
// The first code point
|
||||
sectionBegin = item.sectionName.substring(charOffset, charOffset + codePointSize);
|
||||
} else if ((charOffset + codePointSize) >= item.sectionName.length()) {
|
||||
// The last code point
|
||||
sectionEnd = item.sectionName.substring(charOffset, charOffset + codePointSize);
|
||||
}
|
||||
charOffset += codePointSize;
|
||||
getSectionLetters(nextItem.sectionName, mTmpSections, mTmpSectionBounds);
|
||||
String sectionBegin = mTmpSections[0];
|
||||
String sectionEnd = mTmpSections[1];
|
||||
PointF sectionBeginBounds = mTmpSectionBounds[0];
|
||||
PointF sectionEndBounds = mTmpSectionBounds[1];
|
||||
|
||||
// Calculate where to draw the section
|
||||
int sectionBaseline = (int) (viewTopOffset + sectionBeginBounds.y);
|
||||
int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
|
||||
mPaddingStart;
|
||||
int y = child.getTop() + sectionBaseline;
|
||||
|
||||
// Determine whether this is the last row with apps in that section, if
|
||||
// so, then fix the section to the row allowing it to scroll past the
|
||||
// baseline, otherwise, bound it to the baseline so it's in the viewport
|
||||
int appIndexInSection = items.get(pos).sectionAppIndex;
|
||||
int nextRowPos = Math.min(items.size() - 1,
|
||||
pos + mAppsPerRow - (appIndexInSection % mAppsPerRow));
|
||||
boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(nextItem.sectionName);
|
||||
if (!fixedToRow) {
|
||||
y = Math.max(sectionBaseline, y);
|
||||
}
|
||||
|
||||
Point sectionBeginBounds = getAndCacheSectionBounds(sectionBegin);
|
||||
int minTop = cellTopOffset + sectionBeginBounds.y;
|
||||
int top = child.getTop() + cellTopOffset + sectionBeginBounds.y;
|
||||
int left = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
|
||||
mPaddingStart;
|
||||
int col = appIndexInSection % mAppsPerRow;
|
||||
int nextRowPos = Math.min(pos - col + mAppsPerRow, items.size() - 1);
|
||||
int alpha = 255;
|
||||
boolean fixedToRow = !items.get(nextRowPos).sectionName.equals(item.sectionName);
|
||||
if (fixedToRow) {
|
||||
alpha = Math.min(255, (int) (255 * (Math.max(0, top) / (float) minTop)));
|
||||
} else {
|
||||
// If we aren't fixed to the current row, then bound into the viewport
|
||||
top = Math.max(minTop, top);
|
||||
}
|
||||
if (lastSectionHeight > 0 && top <= (lastSectionTop + lastSectionHeight)) {
|
||||
top += lastSectionTop - top + lastSectionHeight;
|
||||
// In addition, if it overlaps with the last section that was drawn, then
|
||||
// offset it so that it does not overlap
|
||||
if (lastSectionHeight > 0 && y <= (lastSectionTop + lastSectionHeight)) {
|
||||
y += lastSectionTop - y + lastSectionHeight;
|
||||
}
|
||||
|
||||
// Draw the section header
|
||||
if (FADE_OUT_SECTIONS) {
|
||||
int alpha = 255;
|
||||
if (fixedToRow) {
|
||||
alpha = Math.min(255, (int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
|
||||
}
|
||||
mSectionTextPaint.setAlpha(alpha);
|
||||
}
|
||||
if (sectionEnd != null) {
|
||||
Point sectionEndBounds = getAndCacheSectionBounds(sectionEnd);
|
||||
// If there is a range, draw the range
|
||||
c.drawText(sectionBegin + "/" + sectionEnd,
|
||||
left + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, top,
|
||||
x + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, y,
|
||||
mSectionTextPaint);
|
||||
} else {
|
||||
c.drawText(sectionBegin, left + (mStartMargin - sectionBeginBounds.x) / 2, top,
|
||||
c.drawText(sectionBegin, (int) (x + (mStartMargin / 2f) - (sectionBeginBounds.x / 2f)), y,
|
||||
mSectionTextPaint);
|
||||
}
|
||||
lastSectionTop = top;
|
||||
lastSectionHeight = sectionBeginBounds.y + mSectionHeaderOffset;
|
||||
|
||||
lastSectionTop = y;
|
||||
lastSectionHeight = (int) (sectionBeginBounds.y + mSectionHeaderOffset);
|
||||
lastSectionName = nextItem.sectionName;
|
||||
}
|
||||
}
|
||||
if (holder.mIsSectionHeader) {
|
||||
appIndexInSection = 0;
|
||||
} else {
|
||||
appIndexInSection++;
|
||||
i += (sectionInfo.numAppsInSection - item.sectionAppIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,16 +183,50 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private Point getAndCacheSectionBounds(String sectionName) {
|
||||
Point bounds = mCachedSectionBounds.get(sectionName);
|
||||
/**
|
||||
* Given a section name, return the first and last section letters.
|
||||
*/
|
||||
private void getSectionLetters(String sectionName, String[] lettersOut, PointF[] boundsOut) {
|
||||
lettersOut[0] = lettersOut[1] = null;
|
||||
boundsOut[0] = boundsOut[1] = null;
|
||||
if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
|
||||
int charOffset = 0;
|
||||
while (charOffset < sectionName.length()) {
|
||||
int codePoint = sectionName.codePointAt(charOffset);
|
||||
int codePointSize = Character.charCount(codePoint);
|
||||
if (charOffset == 0) {
|
||||
// The first code point
|
||||
lettersOut[0] = sectionName.substring(charOffset, charOffset + codePointSize);
|
||||
boundsOut[0] = getAndCacheSectionBounds(lettersOut[0]);
|
||||
} else if ((charOffset + codePointSize) >= sectionName.length()) {
|
||||
// The last code point
|
||||
lettersOut[1] = sectionName.substring(charOffset, charOffset + codePointSize);
|
||||
boundsOut[0] = getAndCacheSectionBounds(lettersOut[1]);
|
||||
}
|
||||
charOffset += codePointSize;
|
||||
}
|
||||
} else {
|
||||
lettersOut[0] = sectionName;
|
||||
boundsOut[0] = getAndCacheSectionBounds(lettersOut[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a section name, return the first and last section letters.
|
||||
*/
|
||||
private PointF getAndCacheSectionBounds(String sectionName) {
|
||||
PointF bounds = mCachedSectionBounds.get(sectionName);
|
||||
if (bounds == null) {
|
||||
mSectionTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTmpBounds);
|
||||
bounds = new Point(mTmpBounds.width(), mTmpBounds.height());
|
||||
bounds = new PointF(mSectionTextPaint.measureText(sectionName), mTmpBounds.height());
|
||||
mCachedSectionBounds.put(sectionName, bounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether to draw the section for the given child.
|
||||
*/
|
||||
private boolean shouldDrawItemSection(ViewHolder holder, View child, int childIndex,
|
||||
List<AlphabeticalAppsList.AdapterItem> items) {
|
||||
// Ensure item is not already removed
|
||||
@@ -201,19 +245,12 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
}
|
||||
// Ensure we have a holder position
|
||||
int pos = holder.getPosition();
|
||||
if (pos < 0 || pos >= items.size()) {
|
||||
if (pos <= 0 || pos >= items.size()) {
|
||||
return false;
|
||||
}
|
||||
// Ensure this is not a section header
|
||||
if (items.get(pos).isSectionHeader) {
|
||||
return false;
|
||||
}
|
||||
// Only draw the header for the first item in a section, or whenever the sub-sections
|
||||
// changes (if AppsContainerView.GRID_MERGE_SECTIONS is true, but
|
||||
// AppsContainerView.GRID_MERGE_SECTION_HEADERS is false)
|
||||
// Draw the section header for the first item in each section
|
||||
return (childIndex == 0) ||
|
||||
items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader ||
|
||||
(!items.get(pos - 1).sectionName.equals(items.get(pos).sectionName));
|
||||
(items.get(pos - 1).isSectionHeader && !items.get(pos).isSectionHeader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user