如何在Android中制作粘性分区标题(如iOS)?

如何在Android中制作粘性分区标题(如iOS)?,android,ios,listview,scrollview,Android,Ios,Listview,Scrollview,我的具体问题是:我如何才能达到这样的效果: 反弹效果并不重要,但我需要标题的“粘性”效果。我从哪里开始?我能立足于什么?我需要一些我可以在API 8上实现的东西 谢谢。对于这个问题,已有一些解决方案。您所描述的是部分标题,在Android中被称为粘性部分标题 将此添加到app.gradle文件中 compile 'se.emilsjolander:StickyScrollViewItems:1.1.0' 然后,在我的布局中,我将android:tag=“sticky”添加到特定视图中,

我的具体问题是:我如何才能达到这样的效果:

反弹效果并不重要,但我需要标题的“粘性”效果。我从哪里开始?我能立足于什么?我需要一些我可以在API 8上实现的东西


谢谢。

对于这个问题,已有一些解决方案。您所描述的是部分标题,在Android中被称为粘性部分标题


将此添加到app.gradle文件中

compile 'se.emilsjolander:StickyScrollViewItems:1.1.0'
然后,在我的布局中,我将
android:tag=“sticky”
添加到特定视图中,如textview或edittext not LinearLayout,如下所示。它还使用数据绑定,忽略这一点

    <?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="temp"
            type="com.lendingkart.prakhar.lendingkartdemo.databindingmodel.BusinessDetailFragmentModel" />

        <variable
            name="presenter"
            type="com.lendingkart.prakhar.lendingkartdemo.presenters.BusinessDetailsPresenter" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


        <com.lendingkart.prakhar.lendingkartdemo.customview.StickyScrollView
            android:id="@+id/sticky_scroll"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!-- scroll view child goes here -->
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">


                <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    card_view:cardCornerRadius="5dp"
                    card_view:cardUseCompatPadding="true">

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:orientation="vertical">


                        <TextView
                            style="@style/group_view_text"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:background="@drawable/businessdetailtitletextviewbackground"
                            android:padding="@dimen/activity_horizontal_margin"
                            android:tag="sticky"
                            android:text="@string/business_contact_detail" />

                        <android.support.design.widget.TextInputLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_margin="7dp">

                            <android.support.design.widget.TextInputEditText
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                android:hint="@string/comapnyLabel"
                                android:textSize="16sp" />

                        </android.support.design.widget.TextInputLayout>

                        <android.support.design.widget.TextInputLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_margin="5dp">

                            <android.support.design.widget.TextInputEditText
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                android:hint="@string/contactLabel"
                                android:textSize="16sp" />

                        </android.support.design.widget.TextInputLayout>

                        <android.support.design.widget.TextInputLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_margin="5dp">

                            <android.support.design.widget.TextInputEditText
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                android:hint="@string/emailLabel"
                                android:textSize="16sp" />

                        </android.support.design.widget.TextInputLayout>

                        <android.support.design.widget.TextInputLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_margin="5dp">

                            <android.support.design.widget.TextInputEditText
                                android:layout_width="match_parent"
                                android:layout_height="wrap_content"
                                android:hint="@string/NumberOfEmployee"
                                android:textSize="16sp" />

                        </android.support.design.widget.TextInputLayout>


                    </LinearLayout>
                </android.support.v7.widget.CardView>

                <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    card_view:cardCornerRadius="5dp"
                    card_view:cardUseCompatPadding="true">

                    <TextView
                        style="@style/group_view_text"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:background="@drawable/businessdetailtitletextviewbackground"
                        android:padding="@dimen/activity_horizontal_margin"
                        android:tag="sticky"
                        android:text="@string/nature_of_business" />


                </android.support.v7.widget.CardView>

                <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    card_view:cardCornerRadius="5dp"
                    card_view:cardUseCompatPadding="true">

                    <TextView
                        style="@style/group_view_text"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:background="@drawable/businessdetailtitletextviewbackground"
                        android:padding="@dimen/activity_horizontal_margin"
                        android:tag="sticky"
                        android:text="@string/taxation" />


                </android.support.v7.widget.CardView>



            </LinearLayout>
        </com.lendingkart.prakhar.lendingkartdemo.customview.StickyScrollView>


    </LinearLayout>
</layout>

textview的样式组如下所示

 <style name="group_view_text" parent="@android:style/TextAppearance.Medium">
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textColor">@color/edit_text_color</item>
        <item name="android:textSize">16dp</item>
        <item name="android:layout_centerVertical">true</item>
        <item name="android:textStyle">bold</item>
    </style>

包装内容
包装内容
@颜色/编辑\u文本\u颜色
16dp
真的
大胆的
textview的背景是这样的:(@drawable/businessdetailtitletextviewbackground)


编辑:有一些空闲时间添加完整工作示例的代码。相应地编辑了答案

对于那些不想使用第三方代码的人(或者不能直接使用它,例如在Xamarin中),这可以很容易地手工完成。 其思想是使用另一个ListView作为标题。此列表视图仅包含标题项。用户将无法滚动它(setEnabled(false)),但将根据主列表的滚动从代码中滚动。因此,您将有两个列表—headerListview和mainListview,以及两个相应的适配器headerAdapter和mainAdapter。headerAdapter仅返回剖面视图,而mainAdapter支持两种视图类型(剖面和项目)。您需要一个方法,该方法在主列表中占据一个位置,并在节列表中返回相应的位置

主要活动

public class MainActivity extends AppCompatActivity {

    public static final int TYPE_SECTION = 0;
    public static final int TYPE_ITEM = 1;

    ListView mainListView;
    ListView headerListView;
    MainAdapter mainAdapter;
    HeaderAdapter headerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mainListView = (ListView)findViewById(R.id.list);
        headerListView = (ListView)findViewById(R.id.header);
        mainAdapter = new MainAdapter();
        headerAdapter = new HeaderAdapter();

        headerListView.setEnabled(false);
        headerListView.setAdapter(headerAdapter);
        mainListView.setAdapter(mainAdapter);

        mainListView.setOnScrollListener(new AbsListView.OnScrollListener(){

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState){

            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                // this should return an index in the headers list, based one the index in the main list. The logic for this is highly dependent on your data.
                int pos = mainAdapter.getSectionIndexForPosition(firstVisibleItem);
                // this makes sure our headerListview shows the proper section (the one on the top of the mainListview)
                headerListView.setSelection(pos);

                // this makes sure that headerListview is scrolled exactly the same amount as the mainListview
                if(mainAdapter.getItemViewType(firstVisibleItem + 1) == TYPE_SECTION){
                    headerListView.setSelectionFromTop(pos, mainListView.getChildAt(0).getTop());
                }
            }
        });
    }

    public class MainAdapter extends BaseAdapter{
        int count = 30;

        @Override
        public int getItemViewType(int position){
            if((float)position / 10 == (int)((float)position/10)){
                return TYPE_SECTION;
            }else{
                return TYPE_ITEM;
            }
        }

        @Override
        public int getViewTypeCount(){ return 2; }

        @Override
        public int getCount() { return count - 1; }

        @Override
        public Object getItem(int position) { return null; }

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

        public int getSectionIndexForPosition(int position){ return position / 10; }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v =  getLayoutInflater().inflate(R.layout.item, parent, false);
            position++;
            if(getItemViewType(position) == TYPE_SECTION){
                ((TextView)v.findViewById(R.id.text)).setText("SECTION "+position);

            }else{
                ((TextView)v.findViewById(R.id.text)).setText("Item "+position);
            }
            return v;
        }
    }

    public class HeaderAdapter extends BaseAdapter{
        int count = 5;

        @Override
        public int getCount() { return count; }

        @Override
        public Object getItem(int position) { return null; }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v =  getLayoutInflater().inflate(R.layout.item, parent, false);
            ((TextView)v.findViewById(R.id.text)).setText("SECTION "+position*10);
            return v;
        }
    }

}
这里有几件事需要注意。我们不想在主视图列表中显示第一个部分,因为它会产生一个重复的部分(它已经显示在标题中)。为了避免这种情况,请在mainAdapter.getCount()中:

并确保getView()方法中的第一行是

position++;
这样,主列表将呈现除第一个单元格外的所有单元格

另一件事是要确保headerListview的高度与列表项的高度匹配。在本例中,高度是固定的,但如果您的项目高度未设置为dp中的精确值,则可能会很棘手。请参考此答案,了解如何解决此问题:

主布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">
    <ListView
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="48dp"/>

    <ListView
        android:id="@+id/list"
        android:layout_below="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="48dp">
    <TextView
        android:id="@+id/text"
        android:gravity="center_vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

项目/标题布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">
    <ListView
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="48dp"/>

    <ListView
        android:id="@+id/list"
        android:layout_below="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="48dp">
    <TextView
        android:id="@+id/text"
        android:gravity="center_vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

您可以使用库实现此效果。它为您提供了一个用于RecyclerView的布局管理器,具有可互换的线性、网格和交错视图显示

一个好的演示位于

只是为了得到这样的结果

app:slm_headerDisplay="inline|sticky"
or
app:slm_headerDisplay="sticky"

我使用了一个特殊类来实现类似iPhone的listview。 您可以在这里找到源代码示例

已更新listview的此类如下所示

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ListView;
import android.widget.RelativeLayout;

public class HeaderListView extends RelativeLayout {

    // TODO: Handle listViews with fast scroll
    // TODO: See if there are methods to dispatch to mListView

    private static final int FADE_DELAY    = 1000;
    private static final int FADE_DURATION = 2000;

    private InternalListView mListView;
    private SectionAdapter   mAdapter;
    private RelativeLayout   mHeader;
    private View             mHeaderConvertView;
    private FrameLayout      mScrollView;
    private AbsListView.OnScrollListener mExternalOnScrollListener;

    public HeaderListView(Context context) {
        super(context);
        init(context, null);
    }

    public HeaderListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        mListView = new InternalListView(getContext(), attrs);
        LayoutParams listParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        listParams.addRule(ALIGN_PARENT_TOP);
        mListView.setLayoutParams(listParams);
        mListView.setOnScrollListener(new HeaderListViewOnScrollListener());
        mListView.setVerticalScrollBarEnabled(false);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if (mAdapter != null) {
                    mAdapter.onItemClick(parent, view, position, id);
                }
            }
        });
        addView(mListView);

        mHeader = new RelativeLayout(getContext());
        LayoutParams headerParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        headerParams.addRule(ALIGN_PARENT_TOP);
        mHeader.setLayoutParams(headerParams);
        mHeader.setGravity(Gravity.BOTTOM);
        addView(mHeader);

        // The list view's scroll bar can be hidden by the header, so we display our own scroll bar instead
        Drawable scrollBarDrawable = getResources().getDrawable(R.drawable.scrollbar_handle_holo_light);
        mScrollView = new FrameLayout(getContext());
        LayoutParams scrollParams = new LayoutParams(scrollBarDrawable.getIntrinsicWidth(), LayoutParams.MATCH_PARENT);
        scrollParams.addRule(ALIGN_PARENT_RIGHT);
        scrollParams.rightMargin = (int) dpToPx(2);
        mScrollView.setLayoutParams(scrollParams);

        ImageView scrollIndicator = new ImageView(context);
        scrollIndicator.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        scrollIndicator.setImageDrawable(scrollBarDrawable);
        scrollIndicator.setScaleType(ScaleType.FIT_XY);
        mScrollView.addView(scrollIndicator);
        mScrollView.setVisibility(INVISIBLE);

        addView(mScrollView);
    }

    public void setAdapter(SectionAdapter adapter) {
        mAdapter = adapter;
        mListView.setAdapter(adapter);
    }

    public void setOnScrollListener(AbsListView.OnScrollListener l) {
        mExternalOnScrollListener = l;
    }

    private class HeaderListViewOnScrollListener implements AbsListView.OnScrollListener {

        private int            previousFirstVisibleItem = -1;
        private int            direction                = 0;
        private int            actualSection            = 0;
        private boolean        scrollingStart           = false;
        private boolean        doneMeasuring            = false;
        private int            lastResetSection         = -1;
        private int            nextH;
        private int            prevH;
        private View           previous;
        private View           next;
        private AlphaAnimation fadeOut                  = new AlphaAnimation(1f, 0f);
        private boolean        noHeaderUpToHeader       = false;
        private boolean        didScroll = false;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
            if (mExternalOnScrollListener != null) {
                mExternalOnScrollListener.onScrollStateChanged(view, scrollState);
            }
            didScroll = true;
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            if (mExternalOnScrollListener != null) {
                mExternalOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
            }

            if (!didScroll) {
                return;
            }

            firstVisibleItem -= mListView.getHeaderViewsCount();
            if (firstVisibleItem < 0) {
                mHeader.removeAllViews();
                return;
            }

            updateScrollBar();
            if (visibleItemCount > 0 && firstVisibleItem == 0 && mHeader.getChildAt(0) == null) {
                addSectionHeader(0);
                lastResetSection = 0;
            }

            int realFirstVisibleItem = getRealFirstVisibleItem(firstVisibleItem, visibleItemCount);
            if (totalItemCount > 0 && previousFirstVisibleItem != realFirstVisibleItem) {
                direction = realFirstVisibleItem - previousFirstVisibleItem;

                actualSection = mAdapter.getSection(realFirstVisibleItem);

                boolean currIsHeader = mAdapter.isSectionHeader(realFirstVisibleItem);
                boolean prevHasHeader = mAdapter.hasSectionHeaderView(actualSection - 1);
                boolean nextHasHeader = mAdapter.hasSectionHeaderView(actualSection + 1);
                boolean currHasHeader = mAdapter.hasSectionHeaderView(actualSection);
                boolean currIsLast = mAdapter.getRowInSection(realFirstVisibleItem) == mAdapter.numberOfRows(actualSection) - 1;
                boolean prevHasRows = mAdapter.numberOfRows(actualSection - 1) > 0;
                boolean currIsFirst = mAdapter.getRowInSection(realFirstVisibleItem) == 0;

                boolean needScrolling = currIsFirst && !currHasHeader && prevHasHeader && realFirstVisibleItem != firstVisibleItem;
                boolean needNoHeaderUpToHeader = currIsLast && currHasHeader && !nextHasHeader && realFirstVisibleItem == firstVisibleItem && Math.abs(mListView.getChildAt(0).getTop()) >= mListView.getChildAt(0).getHeight() / 2;

                noHeaderUpToHeader = false;
                if (currIsHeader && !prevHasHeader && firstVisibleItem >= 0) {
                    resetHeader(direction < 0 ? actualSection - 1 : actualSection);
                } else if ((currIsHeader && firstVisibleItem > 0) || needScrolling) {
                    if (!prevHasRows) {
                        resetHeader(actualSection-1);
                    }
                    startScrolling();
                } else if (needNoHeaderUpToHeader) {
                    noHeaderUpToHeader = true;
                } else if (lastResetSection != actualSection) {
                    resetHeader(actualSection);
                }

                previousFirstVisibleItem = realFirstVisibleItem;
            }

            if (scrollingStart) {
                int scrolled = realFirstVisibleItem >= firstVisibleItem ? mListView.getChildAt(realFirstVisibleItem - firstVisibleItem).getTop() : 0;

                if (!doneMeasuring) {
                    setMeasurements(realFirstVisibleItem, firstVisibleItem);
                }

                int headerH = doneMeasuring ? (prevH - nextH) * direction * Math.abs(scrolled) / (direction < 0 ? nextH : prevH) + (direction > 0 ? nextH : prevH) : 0;

                mHeader.scrollTo(0, -Math.min(0, scrolled - headerH));
                if (doneMeasuring && headerH != mHeader.getLayoutParams().height) {
                    LayoutParams p = (LayoutParams) (direction < 0 ? next.getLayoutParams() : previous.getLayoutParams());
                    p.topMargin = headerH - p.height;
                    mHeader.getLayoutParams().height = headerH;
                    mHeader.requestLayout();
                }
            }

            if (noHeaderUpToHeader) {
                if (lastResetSection != actualSection) {
                    addSectionHeader(actualSection);
                    lastResetSection = actualSection + 1;
                }
                mHeader.scrollTo(0, mHeader.getLayoutParams().height - (mListView.getChildAt(0).getHeight() + mListView.getChildAt(0).getTop()));
            }
        }

        private void startScrolling() {
            scrollingStart = true;
            doneMeasuring = false;
            lastResetSection = -1;
        }

        private void resetHeader(int section) {
            scrollingStart = false;
            addSectionHeader(section);
            mHeader.requestLayout();
            lastResetSection = section;
        }

        private void setMeasurements(int realFirstVisibleItem, int firstVisibleItem) {

            if (direction > 0) {
                nextH = realFirstVisibleItem >= firstVisibleItem ? mListView.getChildAt(realFirstVisibleItem - firstVisibleItem).getMeasuredHeight() : 0;
            }

            previous = mHeader.getChildAt(0);
            prevH = previous != null ? previous.getMeasuredHeight() : mHeader.getHeight();

            if (direction < 0) {
                if (lastResetSection != actualSection - 1) {
                    addSectionHeader(Math.max(0, actualSection - 1));
                    next = mHeader.getChildAt(0);
                }
                nextH = mHeader.getChildCount() > 0 ? mHeader.getChildAt(0).getMeasuredHeight() : 0;
                mHeader.scrollTo(0, prevH);
            }
            doneMeasuring = previous != null && prevH > 0 && nextH > 0;
        }

        private void updateScrollBar() {
            if (mHeader != null && mListView != null && mScrollView != null) {
                int offset = mListView.computeVerticalScrollOffset();
                int range = mListView.computeVerticalScrollRange();
                int extent = mListView.computeVerticalScrollExtent();
                mScrollView.setVisibility(extent >= range ? View.INVISIBLE : View.VISIBLE);
                if (extent >= range) {
                    return;
                }
                int top = range == 0 ? mListView.getHeight() : mListView.getHeight() * offset / range;
                int bottom = range == 0 ? 0 : mListView.getHeight() - mListView.getHeight() * (offset + extent) / range;
                mScrollView.setPadding(0, top, 0, bottom);
                fadeOut.reset();
                fadeOut.setFillBefore(true);
                fadeOut.setFillAfter(true);
                fadeOut.setStartOffset(FADE_DELAY);
                fadeOut.setDuration(FADE_DURATION);
                mScrollView.clearAnimation();
                mScrollView.startAnimation(fadeOut);
            }
        }

        private void addSectionHeader(int actualSection) {
            View previousHeader = mHeader.getChildAt(0);
            if (previousHeader != null) {
                mHeader.removeViewAt(0);
            }

            if (mAdapter.hasSectionHeaderView(actualSection)) {
                mHeaderConvertView = mAdapter.getSectionHeaderView(actualSection, mHeaderConvertView, mHeader);
                mHeaderConvertView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));

                mHeaderConvertView.measure(MeasureSpec.makeMeasureSpec(mHeader.getWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));

                mHeader.getLayoutParams().height = mHeaderConvertView.getMeasuredHeight();
                mHeaderConvertView.scrollTo(0, 0);
                mHeader.scrollTo(0, 0);
                mHeader.addView(mHeaderConvertView, 0);
            } else {
                mHeader.getLayoutParams().height = 0;
                mHeader.scrollTo(0, 0);
            }

            mScrollView.bringToFront();
        }

        private int getRealFirstVisibleItem(int firstVisibleItem, int visibleItemCount) {
            if (visibleItemCount == 0) {
                return -1;
            }
            int relativeIndex = 0, totalHeight = mListView.getChildAt(0).getTop();
            for (relativeIndex = 0; relativeIndex < visibleItemCount && totalHeight < mHeader.getHeight(); relativeIndex++) {
                totalHeight += mListView.getChildAt(relativeIndex).getHeight();
            }
            int realFVI = Math.max(firstVisibleItem, firstVisibleItem + relativeIndex - 1);
            return realFVI;
        }
    }

    public ListView getListView() {
        return mListView;
    }

    public void addHeaderView(View v) {
        mListView.addHeaderView(v);
    }

    private float dpToPx(float dp) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
    }

    protected class InternalListView extends ListView {

        public InternalListView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        @Override
        protected int computeVerticalScrollExtent() {
            return super.computeVerticalScrollExtent();
        }

        @Override
        protected int computeVerticalScrollOffset() {
            return super.computeVerticalScrollOffset();
        }

        @Override
        protected int computeVerticalScrollRange() {
            return super.computeVerticalScrollRange();
        }
    }
}
导入android.content.Context;
导入android.graphics.drawable.drawable;
导入android.util.AttributeSet;
导入android.util.TypedValue;
导入android.view.Gravity;
导入android.view.view;
导入android.view.animation.AlphaAnimation;
导入android.widget.AbsListView;
导入android.widget.AdapterView;
导入android.widget.FrameLayout;
导入android.widget.ImageView;
导入android.widget.ImageView.ScaleType;
导入android.widget.ListView;
导入android.widget.RelativeLayout;
公共类HeaderListView扩展了RelativeLayout{
//TODO:使用快速滚动处理列表视图
//TODO:查看是否有要分派到mListView的方法
专用静态最终积分衰减_延迟=1000;
专用静态最终积分衰减时间=2000;
私有内部列表视图mListView;
私人部分适配器mAdapter;
私人亲戚;
私有视图mHeaderConvertView;
私有框架视图;
私有AblistView.OnScrollListener mExternalOnScrollListener;
公共标题列表视图(上下文){
超级(上下文);
init(上下文,null);
}
公共标题列表视图(上下文、属性集属性){
超级(上下文,attrs);
init(上下文,attrs);
}
私有void init(上下文上下文、属性集属性){
mListView=newinternallistview(getContext(),attrs);
LayoutParams listParams=新的LayoutParams(LayoutParams.MATCH\u父级,LayoutParams.MATCH\u父级);
listParams.addRule(对齐父项和顶部);
mListView.setLayoutParams(listParams);
mListView.setOnScrollListener(新的HeaderListViewOnScrollListener());
mListView.setVerticalScrollBarEnabled(false);
mListView.setOnItemClickListener(新的AdapterView.OnItemClickListener(){
@凌驾
public void onItemClick(AdapterView父对象、视图、整型位置、长id){
如果(mAdapter!=null){
mAdapter.onItemClick(父对象、视图、位置、id);
}
}
});
addView(mListView);
mHeader=新关系
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.example.parsaniahardik.listview_stickyheader_ios.HeaderListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/lv">

    </com.example.parsaniahardik.listview_stickyheader_ios.HeaderListView>