【问题标题】:Making a middle element to get stuck in the header (ScrollView/ListView)使中间元素卡在标题中(ScrollView/ListView)
【发布时间】:2013-07-22 05:36:07
【问题描述】:

我想制作一个元素,它首先显示在ScrollView(或ListView)的中间,然后在滚动时卡在屏幕标题中。

这是 CSS+JS 中的原型实现:http://jsfiddle.net/minhee/aPcv4/embedded/result/

乍一看,我会让ScrollView 包含ListView,但官方文档说:

您永远不应该将 ScrollView 与 ListView 一起使用,因为 ListView 负责自己的垂直滚动。最重要的是,这样做会破坏 ListView 中处理大型列表的所有重要优化,因为它有效地强制 ListView 显示其整个项目列表以填充 ScrollView 提供的无限容器。

那么,我可以尝试哪些方法来实现这个 UI?

更新:我试过StickyListHeaders,但是:“目前无法在表头中有交互元素,按钮、开关等只有在表头没有卡住的情况下才会起作用。 ”另外,我发现它不太适合这种情况。我不需要多个标题,只需要一个中间元素就可以卡在标题中。

【问题讨论】:

  • 我刚刚找到了这个库:github.com/emilsjolander/StickyListHeaders,所以我现在就试试这个,如果效果好的话,我自己添加答案。
  • 我发现 StickyListHeaders 不适合我的工作,所以我更新了问题。

标签: android android-listview android-scrollview


【解决方案1】:

我过去曾使用(或者更确切地说,尝试使用)StickyListHeaders 库。在遇到一些问题后,我想出了以下内容。这与其他海报的建议没有太大区别。

主布局文件activity_layout.xmlListViewLinearLayout组成,默认情况下是不可见的。使用 OnScrollListener 的 onScroll() 方法,LinearLayout's 可见性被切换。您不需要膨胀另一个布局或将视图动态添加到布局的父级。这就是onScroll 方法的样子:

public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    if (firstVisibleItem > 3) {        // 5th row will stick
        llHeader.setVisibility(View.VISIBLE);
    } else {
        llHeader.setVisibility(View.GONE);
    }
}

只需切换可见性即可获得所需的效果。你可以看看下面的代码。它是您可以期待的工作示例。该活动包含一个ListView,其严格的准系统扩展为BaseAdapterListView 填充有编号按钮(每行一个,从 0 开始,一直到 19)。

public class StickyHeader extends Activity {    

    LinearLayout llHeader;  
    ListView lv;
    SHAdapter shAdapter;

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

        lv = (ListView) findViewById(R.id.listView1);        
        llHeader = (LinearLayout) findViewById(R.id.llHeader);        
        shAdapter = new SHAdapter();        
        lv.setAdapter(shAdapter);

        lv.setOnScrollListener(new OnScrollListener() {

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

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (firstVisibleItem > 3) {
                    llHeader.setVisibility(View.VISIBLE);
                } else {
                    llHeader.setVisibility(View.GONE);
                }
            }
        });
    }   

    public class SHAdapter extends BaseAdapter {

        Button btCurrent;
        int[] arr = new int[] {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};

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

        @Override
        public Object getItem(int arg0) {
            return arr[arg0];
        }

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

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            convertView = getLayoutInflater().inflate(R.layout.list_item_layout, null);         
            btCurrent = (Button) convertView.findViewById(R.id.button1);            

            if ((Integer)getItem(position) == 4) {
                btCurrent.setText("Number " + getItem(position) + " is sticky");
            } else {
                btCurrent.setText("" + getItem(position));
            }

            return convertView;
        }
    }
}

activity_layout.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

    <!-- This LinearLayout's visibility is toggled -->

    <!-- Improvement suggested by user 'ar34z' 
         (see comment section below) --> 
    <include layout="@layout/list_item_layout" />

</RelativeLayout>

list_item_layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/llHeader"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@color/white"
    android:orientation="vertical" >

    <Button
        android:id="@+id/button1"
        android:layout_gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

【讨论】:

  • 不错的答案。为了使其更易于维护,您可以在 activity_layout.xml 中包含 list_item_layout.xml。当布局发生变化时,您所要做的就是更新 list_item_layout.xml。
  • @ar34z 绝对!谢谢,我已经进行了适当的更改。
【解决方案2】:

这是说明主要思想的最简单的代码:

listView.setOnScrollListener(new OnScrollListener() {
    ViewGroup mainView = (ViewGroup) findViewById(R.id.main);
    View pinnedView = null;

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (firstVisibleItem < PINNED_ITEM) {
            mainView.removeView(pinnedView);
            pinnedView = null;
        } else if (pinnedView == null) {
            pinnedView = adapter.getView(PINNED_ITEM, null, view);
            pinnedView.setBackgroundColor(0xFF000000);
            mainView.addView(pinnedView);
        }
    }

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

我有一个FrameLayout,它只包含我的ListView

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/main"
>
    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />
</FrameLayout>

PINNED_ITEM 是您的项目的位置(例如,PINNED_ITEM = 2)。此布局充当列表的叠加层。 ScrollListener 跟踪当前可见项目,如果它检测到一个项目应该被固定,它会将其添加到布局中,否则将其删除。

需要pinnedView.setBackgroundColor(0xFF000000); 行来为项目设置不透明背景。如果您不这样做,该项目将是透明的。您可以根据需要调整背景(例如,您可以使用当前主题属性的背景)。

【讨论】:

    【解决方案3】:

    为了实现这一点,我将 listView 放在一个 relativeLayout 中,并在滚动上添加一个侦听器。然后在其中,当 firstVisibleItem 更改时,我将复制您需要的 itemView 并将其显示在您的 listView 上。

    这是我的想法的一个简短示例。唯一的问题是重复视图必须是不透明的。

    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.view.ViewGroup.LayoutParams;
    import android.widget.AbsListView;
    import android.widget.AbsListView.OnScrollListener;
    import android.widget.ArrayAdapter;
    import android.widget.LinearLayout;
    import android.widget.ListView;
    import android.widget.RelativeLayout;
    
    public class MainActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            /*Create listView inside a relativelayout*/
            final RelativeLayout rl = new RelativeLayout(this);
            final ListView lv = new ListView(this);
            rl.addView(lv);
            /* Set it as a content view*/
            setContentView(rl);
            /*populate it*/
            String[] items = { "Cupcake",
                    "Donut",
                    "Eclair",
                    "Froyo",
                    "Gingerbread",
                    "Honeycomb",
                    "Ice Cream Sandwich",
                    "Jelly Bean"};
            int size = 10;
            String[] arrayItems = new String[items.length*size];
            for(int i = 0; i < arrayItems.length; i++)
                arrayItems[i] = items[i%items.length] + " " + i;     
            /* Need to use a non transparent view*/
            final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                        R.layout.simple_list_item_1_opaque, arrayItems);        
            lv.setAdapter(adapter);
    
            /* Choose the item to stick*/
            final int itemToStick = 3;
    
            /* Create things needed for duplication */
            final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
            params.addRule(RelativeLayout.ALIGN_PARENT_TOP, 1);
            final RelativeLayout.LayoutParams selectorParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, lv.getDividerHeight());
            lv.setOnScrollListener(new OnScrollListener() {
    
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    // TODO Auto-generated method stub
    
                }
    
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem,
                        int visibleItemCount, int totalItemCount) {
                    if(itemToStick <= firstVisibleItem && rl.getChildCount() == 1)
                    {
                        /* Put view in a linearlayout in order to be able to add the sepline */
                        LinearLayout ll = new LinearLayout(view.getContext());
                        ll.setOrientation(LinearLayout.VERTICAL);
                        ll.addView(adapter.getView(itemToStick, null, null),params); 
                        /* Create Divider */
                        View selector = new LinearLayout(view.getContext());
                        selector.setBackground(lv.getDivider());
                        /* add views*/
                        ll.addView(selector,selectorParams);
                        rl.addView(ll,params);
    
                    }
                    /* Remove view when scrolling up to it */
                    else if(itemToStick > firstVisibleItem)
                    {
                        if(rl.getChildCount() > 1)
                            rl.removeViewAt(1);
                    }
                }
            });
        }
    
    }
    

    还有 simple_list_item_1_opaque:

    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@android:id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"
        android:gravity="center_vertical"
        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
        android:minHeight="?android:attr/listPreferredItemHeightSmall"
        android:background="#FFFFFF"
    />
    

    【讨论】:

    • 这是我在我的一个项目中所做的,除了我使用了FrameLayout。效果很好。
    猜你喜欢
    • 2016-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2018-10-12
    相关资源
    最近更新 更多