Fixing issue with fast scroller not showing sub-section headers.
- Removed some old logic to draw merged section headers Change-Id: I2a7ff9948a3dce253d6bdcda89cc9f222faab309
This commit is contained in:
@@ -82,17 +82,30 @@ public class AlphabeticalAppsList {
|
||||
* Info about a section in the alphabetic list
|
||||
*/
|
||||
public static class SectionInfo {
|
||||
// The name of this section
|
||||
public String sectionName;
|
||||
// The number of applications in this section
|
||||
public int numAppsInSection;
|
||||
// The section AdapterItem for this section
|
||||
public AdapterItem sectionItem;
|
||||
public int numApps;
|
||||
// The section break AdapterItem for this section
|
||||
public AdapterItem sectionBreakItem;
|
||||
// The first app AdapterItem for this section
|
||||
public AdapterItem firstAppItem;
|
||||
}
|
||||
|
||||
public SectionInfo(String name) {
|
||||
sectionName = name;
|
||||
/**
|
||||
* Info about a fast scroller section, depending if sections are merged, the fast scroller
|
||||
* sections will not be the same set as the section headers.
|
||||
*/
|
||||
public static class FastScrollSectionInfo {
|
||||
// The section name
|
||||
public String sectionName;
|
||||
// To map the touch (from 0..1) to the index in the app list to jump to in the fast
|
||||
// scroller, we use the fraction in range (0..1) of the app index / total app count.
|
||||
public float appRangeFraction;
|
||||
// The AdapterItem to scroll to for this section
|
||||
public AdapterItem appItem;
|
||||
|
||||
public FastScrollSectionInfo(String sectionName, float appRangeFraction) {
|
||||
this.sectionName = sectionName;
|
||||
this.appRangeFraction = appRangeFraction;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,31 +113,30 @@ public class AlphabeticalAppsList {
|
||||
* Info about a particular adapter item (can be either section or app)
|
||||
*/
|
||||
public static class AdapterItem {
|
||||
/** Section & App properties */
|
||||
// The index of this adapter item in the list
|
||||
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 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
|
||||
// The section for this item
|
||||
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, SectionInfo section) {
|
||||
/** App-only properties */
|
||||
// The section name of this app. Note that there can be multiple items with different
|
||||
// sectionNames in the same section
|
||||
public String sectionName = null;
|
||||
// The index of this app in the section
|
||||
public int sectionAppIndex = -1;
|
||||
// The associated AppInfo for the app
|
||||
public AppInfo appInfo = null;
|
||||
// The index of this app not including sections
|
||||
public int appIndex = -1;
|
||||
|
||||
public static AdapterItem asSectionBreak(int pos, SectionInfo section) {
|
||||
AdapterItem item = new AdapterItem();
|
||||
item.position = pos;
|
||||
item.isSectionHeader = true;
|
||||
item.sectionInfo = section;
|
||||
item.sectionName = section.sectionName;
|
||||
item.appInfo = null;
|
||||
item.appIndex = -1;
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -156,6 +168,7 @@ public class AlphabeticalAppsList {
|
||||
private List<AppInfo> mFilteredApps = new ArrayList<>();
|
||||
private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
|
||||
private List<SectionInfo> mSections = new ArrayList<>();
|
||||
private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
|
||||
private RecyclerView.Adapter mAdapter;
|
||||
private Filter mFilter;
|
||||
private AlphabeticIndexCompat mIndexer;
|
||||
@@ -193,6 +206,13 @@ public class AlphabeticalAppsList {
|
||||
return mSections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns fast scroller sections of all the current filtered applications.
|
||||
*/
|
||||
public List<FastScrollSectionInfo> getFastScrollerSections() {
|
||||
return mFastScrollerSections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current filtered list of applications broken down into their sections.
|
||||
*/
|
||||
@@ -321,10 +341,13 @@ public class AlphabeticalAppsList {
|
||||
mFilteredApps.clear();
|
||||
mSections.clear();
|
||||
mSectionedFilteredApps.clear();
|
||||
mFastScrollerSections.clear();
|
||||
SectionInfo lastSectionInfo = null;
|
||||
String lastSectionName = null;
|
||||
FastScrollSectionInfo lastFastScrollerSectionInfo = null;
|
||||
int position = 0;
|
||||
int appIndex = 0;
|
||||
int sectionAppIndex = 0;
|
||||
int numApps = mApps.size();
|
||||
for (AppInfo info : mApps) {
|
||||
String sectionName = mIndexer.computeSectionName(info.title.toString().trim());
|
||||
|
||||
@@ -334,26 +357,29 @@ public class AlphabeticalAppsList {
|
||||
}
|
||||
|
||||
// Create a new section if necessary
|
||||
if (lastSectionInfo == null || !lastSectionInfo.sectionName.equals(sectionName)) {
|
||||
lastSectionInfo = new SectionInfo(sectionName);
|
||||
sectionAppIndex = 0;
|
||||
if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
|
||||
lastSectionName = sectionName;
|
||||
lastSectionInfo = new SectionInfo();
|
||||
mSections.add(lastSectionInfo);
|
||||
lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName,
|
||||
(float) appIndex / numApps);
|
||||
mFastScrollerSections.add(lastFastScrollerSectionInfo);
|
||||
|
||||
// Create a new section item, this item is used to break the flow of items in the
|
||||
// list
|
||||
AdapterItem sectionItem = AdapterItem.asSection(position++, lastSectionInfo);
|
||||
AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
|
||||
if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) {
|
||||
lastSectionInfo.sectionItem = sectionItem;
|
||||
lastSectionInfo.sectionBreakItem = sectionItem;
|
||||
mSectionedFilteredApps.add(sectionItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an app item
|
||||
AdapterItem appItem = AdapterItem.asApp(position++, lastSectionInfo, sectionName,
|
||||
sectionAppIndex++, info, appIndex++);
|
||||
lastSectionInfo.numAppsInSection++;
|
||||
lastSectionInfo.numApps++, info, appIndex++);
|
||||
if (lastSectionInfo.firstAppItem == null) {
|
||||
lastSectionInfo.firstAppItem = appItem;
|
||||
lastFastScrollerSectionInfo.appItem = appItem;
|
||||
}
|
||||
mSectionedFilteredApps.add(appItem);
|
||||
mFilteredApps.add(info);
|
||||
@@ -365,9 +391,9 @@ public class AlphabeticalAppsList {
|
||||
int sectionAppCount = 0;
|
||||
for (int i = 0; i < mSections.size(); i++) {
|
||||
SectionInfo section = mSections.get(i);
|
||||
String mergedSectionName = section.sectionName;
|
||||
sectionAppCount = section.numAppsInSection;
|
||||
sectionAppCount = section.numApps;
|
||||
int mergeCount = 1;
|
||||
|
||||
// Merge rows if the last app in this section is in a column that is greater than
|
||||
// 0, but less than the min number of apps per row. In addition, apply the
|
||||
// constraint to stop merging if the number of rows in the section is greater than
|
||||
@@ -378,35 +404,25 @@ public class AlphabeticalAppsList {
|
||||
(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);
|
||||
mSectionedFilteredApps.remove(nextSection.sectionBreakItem);
|
||||
int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
|
||||
if (AppsContainerView.GRID_MERGE_SECTION_HEADERS) {
|
||||
// Update the section names for the two sections
|
||||
for (int j = pos; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
|
||||
AdapterItem item = mSectionedFilteredApps.get(j);
|
||||
item.sectionName = mergedSectionName;
|
||||
item.sectionInfo = section;
|
||||
}
|
||||
}
|
||||
// Point the section for these new apps to the merged section
|
||||
for (int j = pos + section.numAppsInSection; j < (pos + section.numAppsInSection + nextSection.numAppsInSection); j++) {
|
||||
int nextPos = pos + section.numApps;
|
||||
for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) {
|
||||
AdapterItem item = mSectionedFilteredApps.get(j);
|
||||
item.sectionInfo = section;
|
||||
item.sectionAppIndex += section.numAppsInSection;
|
||||
item.sectionAppIndex += section.numApps;
|
||||
}
|
||||
|
||||
// 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--;
|
||||
}
|
||||
section.numAppsInSection += nextSection.numAppsInSection;
|
||||
sectionAppCount += nextSection.numAppsInSection;
|
||||
section.numApps += nextSection.numApps;
|
||||
sectionAppCount += nextSection.numApps;
|
||||
mergeCount++;
|
||||
if (mergeCount >= mMaxAllowableMerges) {
|
||||
break;
|
||||
|
||||
@@ -254,8 +254,9 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
|
||||
mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255));
|
||||
mFastScrollTextPaint.getTextBounds(mFastScrollSectionName, 0,
|
||||
mFastScrollSectionName.length(), mFastScrollTextBounds);
|
||||
float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName);
|
||||
canvas.drawText(mFastScrollSectionName,
|
||||
(bgBounds.width() - mFastScrollTextBounds.width()) / 2,
|
||||
(bgBounds.width() - textWidth) / 2,
|
||||
bgBounds.height() - (bgBounds.height() - mFastScrollTextBounds.height()) / 2,
|
||||
mFastScrollTextPaint);
|
||||
canvas.restoreToCount(restoreCount);
|
||||
@@ -285,38 +286,33 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the progress (from 0..1) to the position that should be visible
|
||||
* Maps the touch (from 0..1) to the adapter position that should be visible.
|
||||
*/
|
||||
private String scrollToPositionAtProgress(float progress) {
|
||||
List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
|
||||
if (sections.isEmpty()) {
|
||||
private String scrollToPositionAtProgress(float touchFraction) {
|
||||
// Ensure that we have any sections
|
||||
List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
|
||||
mApps.getFastScrollerSections();
|
||||
if (fastScrollSections.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Find the position of the first application in the section that contains the row at the
|
||||
// current progress
|
||||
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
|
||||
int rowAtProgress = (int) (progress * getNumRows());
|
||||
int rowCount = 0;
|
||||
AlphabeticalAppsList.SectionInfo lastSectionInfo = null;
|
||||
for (AlphabeticalAppsList.SectionInfo section : sections) {
|
||||
int numRowsInSection = (int) Math.ceil((float) section.numAppsInSection / mNumAppsPerRow);
|
||||
if (rowCount + numRowsInSection >= rowAtProgress) {
|
||||
lastSectionInfo = section;
|
||||
AlphabeticalAppsList.FastScrollSectionInfo lastScrollSection = fastScrollSections.get(0);
|
||||
for (int i = 1; i < fastScrollSections.size(); i++) {
|
||||
AlphabeticalAppsList.FastScrollSectionInfo scrollSection = fastScrollSections.get(i);
|
||||
if (lastScrollSection.appRangeFraction <= touchFraction &&
|
||||
touchFraction < scrollSection.appRangeFraction) {
|
||||
break;
|
||||
}
|
||||
rowCount += numRowsInSection;
|
||||
lastScrollSection = scrollSection;
|
||||
}
|
||||
int position = items.indexOf(lastSectionInfo.firstAppItem);
|
||||
|
||||
// Scroll the position into view, anchored at the top of the screen if possible. We call the
|
||||
// scroll method on the LayoutManager directly since it is not exposed by RecyclerView.
|
||||
LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
|
||||
stopScroll();
|
||||
layoutManager.scrollToPositionWithOffset(position, 0);
|
||||
layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0);
|
||||
|
||||
// Return the section name of the row
|
||||
return lastSectionInfo.sectionName;
|
||||
return lastScrollSection.sectionName;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -392,11 +388,11 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
|
||||
int appIndex = 0;
|
||||
int rowCount = 0;
|
||||
for (AlphabeticalAppsList.SectionInfo info : sections) {
|
||||
int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow);
|
||||
if (appIndex + info.numAppsInSection > position) {
|
||||
int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
|
||||
if (appIndex + info.numApps > position) {
|
||||
return rowCount + ((position - appIndex) / mNumAppsPerRow);
|
||||
}
|
||||
appIndex += info.numAppsInSection;
|
||||
appIndex += info.numApps;
|
||||
rowCount += numRowsInSection;
|
||||
}
|
||||
return appIndex;
|
||||
@@ -409,7 +405,7 @@ public class AppsContainerRecyclerView extends BaseContainerRecyclerView {
|
||||
List<AlphabeticalAppsList.SectionInfo> sections = mApps.getSections();
|
||||
int rowCount = 0;
|
||||
for (AlphabeticalAppsList.SectionInfo info : sections) {
|
||||
int numRowsInSection = (int) Math.ceil((float) info.numAppsInSection / mNumAppsPerRow);
|
||||
int numRowsInSection = (int) Math.ceil((float) info.numApps / mNumAppsPerRow);
|
||||
rowCount += numRowsInSection;
|
||||
}
|
||||
return rowCount;
|
||||
|
||||
@@ -46,7 +46,6 @@ public class AppsContainerView extends BaseContainerView implements DragSource,
|
||||
View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
public static final boolean GRID_MERGE_SECTIONS = true;
|
||||
public static final boolean GRID_MERGE_SECTION_HEADERS = false;
|
||||
public static final boolean GRID_HIDE_SECTION_HEADERS = false;
|
||||
|
||||
private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.content.Context;
|
||||
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;
|
||||
@@ -86,8 +85,6 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
|
||||
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) {
|
||||
@@ -111,26 +108,24 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
|
||||
// Draw all the sections for this index
|
||||
String lastSectionName = item.sectionName;
|
||||
for (int j = item.sectionAppIndex; j < sectionInfo.numAppsInSection;j++, pos++) {
|
||||
for (int j = item.sectionAppIndex; j < sectionInfo.numApps; j++, pos++) {
|
||||
AlphabeticalAppsList.AdapterItem nextItem = items.get(pos);
|
||||
String sectionName = nextItem.sectionName;
|
||||
if (nextItem.sectionInfo != sectionInfo) {
|
||||
break;
|
||||
}
|
||||
if (j > item.sectionAppIndex && nextItem.sectionName.equals(lastSectionName)) {
|
||||
if (j > item.sectionAppIndex && sectionName.equals(lastSectionName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the section code points
|
||||
getSectionLetters(nextItem.sectionName, mTmpSections, mTmpSectionBounds);
|
||||
String sectionBegin = mTmpSections[0];
|
||||
String sectionEnd = mTmpSections[1];
|
||||
PointF sectionBeginBounds = mTmpSectionBounds[0];
|
||||
PointF sectionEndBounds = mTmpSectionBounds[1];
|
||||
PointF sectionBounds = getAndCacheSectionBounds(sectionName);
|
||||
|
||||
// Calculate where to draw the section
|
||||
int sectionBaseline = (int) (viewTopOffset + sectionBeginBounds.y);
|
||||
int sectionBaseline = (int) (viewTopOffset + sectionBounds.y);
|
||||
int x = mIsRtl ? parent.getWidth() - mPaddingStart - mStartMargin :
|
||||
mPaddingStart;
|
||||
x += (int) ((mStartMargin - sectionBounds.x) / 2f);
|
||||
int y = child.getTop() + sectionBaseline;
|
||||
|
||||
// Determine whether this is the last row with apps in that section, if
|
||||
@@ -139,7 +134,8 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
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);
|
||||
AlphabeticalAppsList.AdapterItem nextRowItem = items.get(nextRowPos);
|
||||
boolean fixedToRow = !sectionName.equals(nextRowItem.sectionName);
|
||||
if (!fixedToRow) {
|
||||
y = Math.max(sectionBaseline, y);
|
||||
}
|
||||
@@ -154,25 +150,18 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
if (FADE_OUT_SECTIONS) {
|
||||
int alpha = 255;
|
||||
if (fixedToRow) {
|
||||
alpha = Math.min(255, (int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
|
||||
alpha = Math.min(255,
|
||||
(int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
|
||||
}
|
||||
mSectionTextPaint.setAlpha(alpha);
|
||||
}
|
||||
if (sectionEnd != null) {
|
||||
// If there is a range, draw the range
|
||||
c.drawText(sectionBegin + "/" + sectionEnd,
|
||||
x + (mStartMargin - sectionBeginBounds.x - sectionEndBounds.x) / 2, y,
|
||||
mSectionTextPaint);
|
||||
} else {
|
||||
c.drawText(sectionBegin, (int) (x + (mStartMargin / 2f) - (sectionBeginBounds.x / 2f)), y,
|
||||
mSectionTextPaint);
|
||||
}
|
||||
c.drawText(sectionName, x, y, mSectionTextPaint);
|
||||
|
||||
lastSectionTop = y;
|
||||
lastSectionHeight = (int) (sectionBeginBounds.y + mSectionHeaderOffset);
|
||||
lastSectionName = nextItem.sectionName;
|
||||
lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset);
|
||||
lastSectionName = sectionName;
|
||||
}
|
||||
i += (sectionInfo.numAppsInSection - item.sectionAppIndex);
|
||||
i += (sectionInfo.numApps - item.sectionAppIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,35 +173,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Given a section name, return the bounds of the given section name.
|
||||
*/
|
||||
private PointF getAndCacheSectionBounds(String sectionName) {
|
||||
PointF bounds = mCachedSectionBounds.get(sectionName);
|
||||
|
||||
Reference in New Issue
Block a user