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:
Winson Chung
2015-05-08 12:04:45 -07:00
parent 3253bf4088
commit 99d96ba6c8
4 changed files with 100 additions and 128 deletions
@@ -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;
+15 -54
View File
@@ -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);