PinnedHeaderListView滚动和头的问题 [英] PinnedHeaderListView scrolling and header issues

查看:442
本文介绍了PinnedHeaderListView滚动和头的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图模仿棒棒糖的联系人应用程序显示了联系人的首字母固定标题的方式,因为我已经写的 这里

I'm trying to mimic the way that the contacts app of Lollipop shows the pinned headers for the contacts' first letter, as i've written about here.

由于原来的code(这是发现 <强>这里 ,在PinnedHeaderListViewSample文件夹中)没有显示出比那些英文其他字母,我不得不改变codeA一点,但是这还不够。这同样适用于标题本身,它必须是在左边,现在除了是行以上。

Since the original code (which is found here, in the "PinnedHeaderListViewSample" folder) doesn't show letters other than the English ones, I had to change the code a bit, but that wasn't enough. Same goes for the header itself, which had to be on the left now other than being above the rows.

一切工作正常,直到我已经测试了RTL语言(希伯来语在我的情况),而设备的区域也被更改为RTL语言(希伯来语在我的情况)。

Everything worked fine, till I've tested it on RTL languages (Hebrew in my case), while the device's locale was also changed to an RTL language (Hebrew in my case) .

由于某些原因,事情就在滚动和头本身既很古怪,和怪异的部分是,它出现在某些设备/ Android的版本。

For some reason, things get really weird in both the scrolling and the header itself, and the weird part is that it occurs on some devices/versions of Android.

例如,在银河S3与奇巧,滚动和滚动条是完全错误的(我滚动到顶部,但滚动条的位置是在中间)。

For example, on Galaxy S3 with Kitkat, the scrolling and the scrollbar is completely wrong (I scroll to the top, yet the scrollbar's location is on the middle).

在LG G2采用Android 4.2.2,它也有这个问题,但它也不会显示标题(除了固定头),特别是那些没有在希伯来文。

On LG G2 with Android 4.2.2, it also has this problem, but it also doesn't show the headers (except for the pinned header), especially not those in Hebrew.

在银河S4和Huwawei登高P7(都运行奇巧),一切都工作得很好,无论我做什么。

On Galaxy S4 and on Huwawei Ascend P7 (both running Kitkat), everything worked fine no matter what I did.

在短,特殊情况是:


  1. 使用一个pinnedHeaderListView

  2. 有使用RTL语言环境的设备,或者通过开发商设置做

  3. 有英文和希伯来文列表视图项

  4. 设置列表视图显示快速滚动。

  5. 使用或者快速滚动,或像你这样没有它的滚动的ListView。

在code量是非常大的,再加上我做了2的POC,而其中之一是,我已经开始与code完全不同的(以使它看起来像棒棒糖)。所以我会尽力展现最少量。

The code

The code amount is very large, plus I've made 2 POCs, while one of them is quite different from the code that I've started with (to make it look like on Lollipop). so I'll try to show the minimal amount.

编辑:大POC code可在Github上, 这里

the big POC code is available on Github, here .

PinnedHeaderActivity.java

我添加了2项希伯来语的顶部,进入名称字段:

I've added 2 Hebrew items to the top, into the "names" field:

        "אאא",
        "בבב",

在setupListView的方法,我做了快速滚动条可见:

in "setupListView" method, I've made the fast scrollbar visible:

    listView.setFastScrollEnabled(true);

在NamesAdapterCTOR,我将它支持比英文字母更多:

in "NamesAdapter" CTOR, I've made it support more than the English Alphabet:

    public NamesAdapter(Context context, int resourceId, int textViewResourceId, String[] objects) {
        super(context, resourceId, textViewResourceId, objects);
        final SortedSet<Character> set = new TreeSet<Character>();
        for (final String string : objects) {
            final String trimmed = string == null ? "" : string.trim();
            if (!TextUtils.isEmpty(trimmed))
                set.add(Character.toUpperCase(trimmed.charAt(0)));
            else
                set.add(' ');
        }
        final StringBuilder sb = new StringBuilder();
        for (final Character character : set)
            sb.append(character);
        this.mIndexer = new StringArrayAlphabetIndexer(objects, sb.toString());
    }

StringArrayAlphabetIndexer.java

在getSectionForPosition的方法,我把它改为:

In "getSectionForPosition" method, I've changed it to:

public int getSectionForPosition(int position) {
    try {
        if (mArray == null || mArray.length == 0)
            return 0;
        final String curName = mArray[position];
        // Linear search, as there are only a few items in the section index
        // Could speed this up later if it actually gets used.
        // TODO use binary search
        for (int i = 0; i < mAlphabetLength; ++i) {
            final char letter = mAlphabet.charAt(i);
            if (TextUtils.isEmpty(curName) && letter == ' ')
                return i;
            final String targetLetter = Character.toString(letter);
            if (compare(curName, targetLetter) == 0)
                return i;
        }
        return 0; // Don't recognize the letter - falls under zero'th section
    } catch (final Exception ex) {
        return 0;
    }
}

list_item.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list_item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <include layout="@layout/list_item_header" />

        <include
            layout="@android:layout/simple_list_item_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="50dp" />
    </FrameLayout>

    <View
        android:id="@+id/list_divider"
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@android:drawable/divider_horizontal_dark" />

</LinearLayout>

list_item_header.xml

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/header_text"
    android:layout_width="25dip"
    android:layout_height="25dip"
    android:textStyle="bold"
    android:background="@color/pinned_header_background"
    android:textColor="@color/pinned_header_text"
    android:textSize="14sp"
    android:paddingLeft="6dip"
    android:gravity="center" />

下面是截图2,一个并不好看,另一个看起来不错:

Here are 2 screenshots, one that doesn't look good, and another that looks ok:

银河S3奇巧,也是LG G2 4.2.2 - 不显示希伯来文头,并且有怪异的滚动底部附近(相比却会非常快的底部滚动):

Galaxy S3 kitkat and also LG G2 4.2.2 - don't show Hebrew headers, and have weird scrolling near the bottom (goes very fast to the bottom compared to the rest of the scrolling):

银河S4奇巧 - 显示标题罚款,但滚动怪异的底部:

Galaxy S4 kitkat - shows the headers fine, but the scrolling is weird at the bottom:


出于某种原因,银河S4并未反映用户界面,因为它应该,即使我选择了它的开发选项,因此它也可能是为什么它显示了头的原因精细。

For some reason, the Galaxy S4 didn't mirror the UI as it should, even though I've chosen it on the developer options, so it might also be the reason for why it showed the headers fine.

除了尝试2的POC,我做了(一说是更类似材料的设计风格,它是更为复杂的),我已经试过各种方法利用布局和也试图以使用的layoutDirection给力的标题来显示。

Besides trying out 2 POCs that I've made (one that is much more similar to the material design style and it's more complex), I've tried various ways to use the layouts, and also tried to use LayoutDirection in order to force the headers to show.

在更难的问题是解决快速滚动,其工作方式很奇怪的更复杂的POC和了一下简单的一(其中滚动快接近底部)怪异。

The even harder problem is to solve the fast-scrollbar, which works really weird on the more complex POC and a bit weird on the simple one (which scrolls fast near the bottom).

什么是解决这些问题的正确方法?

What is the correct way to solve those issues?

为什么RTL有这种类型的用户界面的问题是什么?

Why does RTL have issues with this type of UI ?

编辑:看来,即使谷歌的例子没有处理RTL项目以及一个简单的ListView:

It seems that even Google's example doesn't handle RTL items well on a simple ListView:

http://developer.android.com/training/contacts-provider/retrieve-names.html

当它接触希伯来语,滚动条云疯狂。

When it has Hebrew contacts, the scroller goes "crazy".

推荐答案

好吧,我不知道谷歌确实有自code是非常联合国可读,所以我做了我自己的类,它工作正常

OK, I have no idea what Google did there since the code is very un-readable, so I made my own class, and it works fine.

这是你必须记住的唯一的事情就是将它们发送到我的课前对项目进行排序,如果你希望仅大写字母为标题,您必须将项目相应地进行排序(以便以a开头的所有项目特定信将在同一组块,不管它是否大写或不是)。

The only thing that you must remember is to sort the items before sending them to my class, and if you wish to have only uppercase letters for the headers, you must sort the items accordingly (so that all items that start with a specific letter will be in the same chunk, no matter if it's uppercase or not).

该解决方案可在GitHub上,在这里:
https://github.com/AndroidDeveloperLB/ListViewVariants

The solution is available on GitHub, here: https://github.com/AndroidDeveloperLB/ListViewVariants

这里的code:

StringArrayAlphabetIndexer

public class StringArrayAlphabetIndexer extends SectionedSectionIndexer
  {
  /**
   * @param items                   each of the items. Note that they must be sorted in a way that each chunk will belong to
   *                                a specific header. For example, chunk with anything that starts with "A"/"a", then a chunk
   *                                that all of its items start with "B"/"b" , etc...
   * @param useOnlyUppercaseHeaders whether the header will be in uppercase or not.
   *                                if true, you must order the items so that each chunk will have its items start with either the lowercase or uppercase letter
   */
  public StringArrayAlphabetIndexer(String[] items,boolean useOnlyUppercaseHeaders)
    {
    super(createSectionsFromStrings(items,useOnlyUppercaseHeaders));
    }

  private static SimpleSection[] createSectionsFromStrings(String[] items,boolean useOnlyUppercaseHeaders)
    {
    //get all of the headers of the sections and their sections-items:
    Map<String,ArrayList<String>> headerToSectionItemsMap=new HashMap<String,ArrayList<String>>();
    Set<String> alphabetSet=new TreeSet<String>();
    for(String item : items)
      {
      String firstLetter=TextUtils.isEmpty(item)?" ":useOnlyUppercaseHeaders?item.substring(0,1).toUpperCase(Locale.getDefault()):
          item.substring(0,1);
      ArrayList<String> sectionItems=headerToSectionItemsMap.get(firstLetter);
      if(sectionItems==null)
        headerToSectionItemsMap.put(firstLetter,sectionItems=new ArrayList<String>());
      sectionItems.add(item);
      alphabetSet.add(firstLetter);
      }
    //prepare the sections, and also sort each section's items :
    SimpleSection[] sections=new SimpleSection[alphabetSet.size()];
    int i=0;
    for(String headerTitle : alphabetSet)
      {
      ArrayList<String> sectionItems=headerToSectionItemsMap.get(headerTitle);
      SimpleSection simpleSection=new AlphaBetSection(sectionItems);
      simpleSection.setName(headerTitle);
      sections[i++]=simpleSection;
      }
    return sections;
    }

  public static class AlphaBetSection extends SimpleSection
    {
    private ArrayList<String> items;

    private AlphaBetSection(ArrayList<String> items)
      {
      this.items=items;
      }

    @Override
    public int getItemsCount()
      {
      return items.size();
      }

    @Override
    public String getItem(int posInSection)
      {
      return items.get(posInSection);
      }

    }


  }

SectionedSectionIndexer

public class SectionedSectionIndexer implements SectionIndexer {
    private final SimpleSection[] mSectionArray;

    public SectionedSectionIndexer(final SimpleSection[] sections) {
        mSectionArray = sections;
        //
        int previousIndex = 0;
        for (int i = 0; i < mSectionArray.length; ++i) {
            mSectionArray[i].startIndex = previousIndex;
            previousIndex += mSectionArray[i].getItemsCount();
            mSectionArray[i].endIndex = previousIndex - 1;
        }
    }

    @Override
    public int getPositionForSection(final int section) {
        final int result = section < 0 || section >= mSectionArray.length ? -1 : mSectionArray[section].startIndex;
        return result;
    }

    /** given a flat position, returns the position within the section */
    public int getPositionInSection(final int flatPos) {
        final int sectionForPosition = getSectionForPosition(flatPos);
        final SimpleSection simpleSection = mSectionArray[sectionForPosition];
        return flatPos - simpleSection.startIndex;
    }

    @Override
    public int getSectionForPosition(final int flatPos) {
        if (flatPos < 0)
            return -1;
        int start = 0, end = mSectionArray.length - 1;
        int piv = (start + end) / 2;
        while (true) {
            final SimpleSection section = mSectionArray[piv];
            if (flatPos >= section.startIndex && flatPos <= section.endIndex)
                return piv;
            if (piv == start && start == end)
                return -1;
            if (flatPos < section.startIndex)
                end = piv - 1;
            else
                start = piv + 1;
            piv = (start + end) / 2;
        }
    }

    @Override
    public SimpleSection[] getSections() {
        return mSectionArray;
    }

    public Object getItem(final int flatPos) {
        final int sectionIndex = getSectionForPosition(flatPos);
        final SimpleSection section = mSectionArray[sectionIndex];
        final Object result = section.getItem(flatPos - section.startIndex);
        return result;
    }

    public Object getItem(final int sectionIndex, final int positionInSection) {
        final SimpleSection section = mSectionArray[sectionIndex];
        final Object result = section.getItem(positionInSection);
        return result;
    }

    public int getRawPosition(final int sectionIndex, final int positionInSection) {
        final SimpleSection section = mSectionArray[sectionIndex];
        return section.startIndex + positionInSection;
    }

    public int getItemsCount() {
        if (mSectionArray.length == 0)
            return 0;
        return mSectionArray[mSectionArray.length - 1].endIndex + 1;
    }

    // /////////////////////////////////////////////
    // Section //
    // //////////
    public static abstract class SimpleSection {
        private String name;
        private int startIndex, endIndex;

        public SimpleSection() {
        }

        public SimpleSection(final String sectionName) {
            this.name = sectionName;
        }

        public String getName() {
            return name;
        }

        public void setName(final String name) {
            this.name = name;
        }

        public abstract int getItemsCount();

        public abstract Object getItem(int posInSection);

  @Override
  public String toString()
    {
    return name;
    }
  }

}

BasePinnedHeaderListViewAdapter

public abstract class BasePinnedHeaderListViewAdapter extends BaseAdapter implements SectionIndexer, OnScrollListener,
    PinnedHeaderListView.PinnedHeaderAdapter
  {
    private SectionIndexer _sectionIndexer;
    private boolean mHeaderViewVisible = true;

    public void setSectionIndexer(final SectionIndexer sectionIndexer) {
        _sectionIndexer = sectionIndexer;
    }

    /** remember to call bindSectionHeader(v,position); before calling return */
    @Override
    public abstract View getView(final int position, final View convertView, final ViewGroup parent);

    public abstract CharSequence getSectionTitle(int sectionIndex);

    protected void bindSectionHeader(final TextView headerView, final View dividerView, final int position) {
        final int sectionIndex = getSectionForPosition(position);
        if (getPositionForSection(sectionIndex) == position) {
            final CharSequence title = getSectionTitle(sectionIndex);
            headerView.setText(title);
            headerView.setVisibility(View.VISIBLE);
            if (dividerView != null)
                dividerView.setVisibility(View.GONE);
        } else {
            headerView.setVisibility(View.GONE);
            if (dividerView != null)
                dividerView.setVisibility(View.VISIBLE);
        }
        // move the divider for the last item in a section
        if (dividerView != null)
            if (getPositionForSection(sectionIndex + 1) - 1 == position)
                dividerView.setVisibility(View.GONE);
            else
                dividerView.setVisibility(View.VISIBLE);
        if (!mHeaderViewVisible)
            headerView.setVisibility(View.GONE);
    }

    @Override
    public int getPinnedHeaderState(final int position) {
        if (_sectionIndexer == null || getCount() == 0 || !mHeaderViewVisible)
            return PINNED_HEADER_GONE;
        if (position < 0)
            return PINNED_HEADER_GONE;
        // The header should get pushed up if the top item shown
        // is the last item in a section for a particular letter.
        final int section = getSectionForPosition(position);
        final int nextSectionPosition = getPositionForSection(section + 1);
        if (nextSectionPosition != -1 && position == nextSectionPosition - 1)
            return PINNED_HEADER_PUSHED_UP;
        return PINNED_HEADER_VISIBLE;
    }

    public void setHeaderViewVisible(final boolean isHeaderViewVisible) {
        mHeaderViewVisible = isHeaderViewVisible;
    }

    public boolean isHeaderViewVisible() {
        return this.mHeaderViewVisible;
    }

    @Override
    public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
            final int totalItemCount) {
        ((PinnedHeaderListView) view).configureHeaderView(firstVisibleItem);
    }

    @Override
    public void onScrollStateChanged(final AbsListView arg0, final int arg1) {
    }

    @Override
    public int getPositionForSection(final int sectionIndex) {
        if (_sectionIndexer == null)
            return -1;
        return _sectionIndexer.getPositionForSection(sectionIndex);
    }

    @Override
    public int getSectionForPosition(final int position) {
        if (_sectionIndexer == null)
            return -1;
        return _sectionIndexer.getSectionForPosition(position);
    }

    @Override
    public Object[] getSections() {
        if (_sectionIndexer == null)
            return new String[] { " " };
        return _sectionIndexer.getSections();
    }

    @Override
    public long getItemId(final int position) {
        return position;
    }
}

IndexedPinnedHeaderListViewAdapter

public abstract class IndexedPinnedHeaderListViewAdapter extends BasePinnedHeaderListViewAdapter
  {
  private int _pinnedHeaderBackgroundColor;
  private int _pinnedHeaderTextColor;

  public void setPinnedHeaderBackgroundColor(final int pinnedHeaderBackgroundColor)
    {
    _pinnedHeaderBackgroundColor=pinnedHeaderBackgroundColor;
    }

  public void setPinnedHeaderTextColor(final int pinnedHeaderTextColor)
    {
    _pinnedHeaderTextColor=pinnedHeaderTextColor;
    }

  @Override
  public CharSequence getSectionTitle(final int sectionIndex)
    {
    return getSections()[sectionIndex].toString();
    }

  @Override
  public void configurePinnedHeader(final View v,final int position,final int alpha)
    {
    final TextView header=(TextView)v;
    final int sectionIndex=getSectionForPosition(position);
    final Object[] sections=getSections();
    if(sections!=null&&sections.length!=0)
      {
      final CharSequence title=getSectionTitle(sectionIndex);
      header.setText(title);
      }
    if(VERSION.SDK_INT<VERSION_CODES.HONEYCOMB)
      if(alpha==255)
        {
        header.setBackgroundColor(_pinnedHeaderBackgroundColor);
        header.setTextColor(_pinnedHeaderTextColor);
        }
      else
        {
        header.setBackgroundColor(Color.argb(alpha,Color.red(_pinnedHeaderBackgroundColor),
            Color.green(_pinnedHeaderBackgroundColor),Color.blue(_pinnedHeaderBackgroundColor)));
        header.setTextColor(Color.argb(alpha,Color.red(_pinnedHeaderTextColor),
            Color.green(_pinnedHeaderTextColor),Color.blue(_pinnedHeaderTextColor)));
        }
    else
      {
      header.setBackgroundColor(_pinnedHeaderBackgroundColor);
      header.setTextColor(_pinnedHeaderTextColor);
      header.setAlpha(alpha/255.0f);
      }
    }

  }

这篇关于PinnedHeaderListView滚动和头的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆