18

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

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

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

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

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

更新:我尝试了 StickyListHeaders,但是:“目前无法在标题中包含交互式元素,按钮、开关等仅在标题未卡住时才有效。” 另外,我发现它不太适合这种情况。我不需要多个标题,只需要一个中间元素就可以卡在标题中。

4

3 回答 3

6

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

主布局文件由默认不可见activity_layout.xml的 aListView和 a组成。LinearLayout使用 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具有严格准系统扩展的BaseAdapter. 填充有编号按钮(ListView每行一个,从 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;
        }
    }
}

活动布局.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>
于 2013-07-28T05:35:33.487 回答
1

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

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);来为项目设置不透明的背景。如果您不这样做,该项目将是透明的。您可以根据需要调整背景(例如,您可以使用当前主题属性的背景)。

于 2013-07-24T10:43:25.577 回答
0

为了做到这一点,我将 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"
/>
于 2013-07-24T09:52:46.933 回答