使中间元素卡在标题中(ScrollView / ListView)

时间:2013-07-22 05:36:08

标签: android android-listview android-scrollview

我希望在第一个中间显示一个显示在ScrollView(或ListView)中间的元素,然后在滚动时卡在屏幕的标题中。

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

乍一看,我会ScrollView加入ListView,但官方文档说:

  

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

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

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

3 个答案:

答案 0 :(得分:6)

我过去曾使用(或者更确切地说,尝试使用)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>

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

答案 2 :(得分: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"
/>