我希望在第一个中间显示一个显示在ScrollView
(或ListView
)中间的元素,然后在滚动时卡在屏幕的标题中。
这是CSS + JS中的原型实现:http://jsfiddle.net/minhee/aPcv4/embedded/result/。
乍一看,我会ScrollView
加入ListView
,但官方文档说:
你永远不应该使用带有ListView的ScrollView,因为ListView负责自己的垂直滚动。最重要的是,这样做会使ListView中的所有重要优化都无法处理大型列表,因为它有效地强制ListView显示其整个项目列表以填充ScrollView提供的无限容器。
那么,我可以尝试用什么方法来实现这个UI?
更新:我尝试了StickyListHeaders,但是:“目前无法在标题中使用交互式元素,按钮,开关等仅在标题不是时才有效卡住了。“另外,我发现它不适合这种情况。我不需要多个标题,但只需要一个中间元素卡在标题中。
答案 0 :(得分:6)
我过去曾使用(或者更确切地说,尝试使用)StickyListHeaders
库。在遇到一些问题之后,我想出了以下内容。它与其他海报的建议没什么不同。
主要布局文件activity_layout.xml
由ListView
和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;
}
}
}
<强> 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"
/>