在ExpandableListView中固定组

有没有一个标准的方式来将组项目滚动到屏幕的顶部,而滚动组的项目。 我看到与ListView类似的例子。 我应该实现什么接口或覆盖哪些方法?

Solutions Collecting From Web of "在ExpandableListView中固定组"

我发现基于Peter Kuterna的固定页眉ListView和android示例ExpandableList1.java的解决scheme。 PinnedHeaderExpListView.java

package com.example; import com.example.ExpandableList.MyExpandableListAdapter; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.TextView; /** * A ListView that maintains a header pinned at the top of the list. The * pinned header can be pushed up and dissolved as needed. */ public class PinnedHeaderExpListView extends ExpandableListView{ /** * Adapter interface. The list adapter must implement this interface. */ public interface PinnedHeaderAdapter { /** * Pinned header state: don't show the header. */ public static final int PINNED_HEADER_GONE = 0; /** * Pinned header state: show the header at the top of the list. */ public static final int PINNED_HEADER_VISIBLE = 1; /** * Pinned header state: show the header. If the header extends beyond * the bottom of the first shown element, push it up and clip. */ public static final int PINNED_HEADER_PUSHED_UP = 2; /** * Configures the pinned header view to match the first visible list item. * * @param header pinned header view. * @param position position of the first visible list item. * @param alpha fading of the header view, between 0 and 255. */ void configurePinnedHeader(View header, int position, int alpha); } private static final int MAX_ALPHA = 255; private MyExpandableListAdapter mAdapter; private View mHeaderView; private boolean mHeaderViewVisible; private int mHeaderViewWidth; private int mHeaderViewHeight; public PinnedHeaderExpListView(Context context) { super(context); } public PinnedHeaderExpListView(Context context, AttributeSet attrs) { super(context, attrs); } public PinnedHeaderExpListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setPinnedHeaderView(View view) { mHeaderView = view; // Disable vertical fading when the pinned header is present // TODO change ListView to allow separate measures for top and bottom fading edge; // in this particular case we would like to disable the top, but not the bottom edge. if (mHeaderView != null) { setFadingEdgeLength(0); } requestLayout(); } @Override public void setAdapter(ExpandableListAdapter adapter) { super.setAdapter(adapter); mAdapter = (MyExpandableListAdapter)adapter; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mHeaderView != null) { measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec); mHeaderViewWidth = mHeaderView.getMeasuredWidth(); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mHeaderView != null) { mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight); configureHeaderView(getFirstVisiblePosition()); } } /** * animating header pushing * @param position */ public void configureHeaderView(int position) { final int group = getPackedPositionGroup(getExpandableListPosition(position)); int groupView = getFlatListPosition(getPackedPositionForGroup(group)); if (mHeaderView == null) { return; } mHeaderView.setOnClickListener(new OnClickListener() { public void onClick(View header) { if(!expandGroup(group)) collapseGroup(group); } }); int state,nextSectionPosition = getFlatListPosition(getPackedPositionForGroup(group+1)); if (mAdapter.getGroupCount()== 0) { state = PinnedHeaderAdapter.PINNED_HEADER_GONE; }else if (position < 0) { state = PinnedHeaderAdapter.PINNED_HEADER_GONE; }else if (nextSectionPosition != -1 && position == nextSectionPosition - 1) { state=PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP; }else state=PinnedHeaderAdapter.PINNED_HEADER_VISIBLE; switch (state) { case PinnedHeaderAdapter.PINNED_HEADER_GONE: { mHeaderViewVisible = false; break; } case PinnedHeaderAdapter.PINNED_HEADER_VISIBLE: { mAdapter.configurePinnedHeader(mHeaderView, group, MAX_ALPHA); if (mHeaderView.getTop() != 0) { mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight); } mHeaderViewVisible = true; break; } case PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP: { View firstView = getChildAt(0); if(firstView==null){ if (mHeaderView.getTop() != 0) { mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight); } mHeaderViewVisible = true; break; } int bottom = firstView.getBottom(); int itemHeight = firstView.getHeight(); int headerHeight = mHeaderView.getHeight(); int y; int alpha; if (bottom < headerHeight) { y = (bottom - headerHeight); alpha = MAX_ALPHA * (headerHeight + y) / headerHeight; } else { y = 0; alpha = MAX_ALPHA; } mAdapter.configurePinnedHeader(mHeaderView, group, alpha); //выползание if (mHeaderView.getTop() != y) { mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y); } mHeaderViewVisible = true; break; } } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mHeaderViewVisible) { drawChild(canvas, mHeaderView, getDrawingTime()); } } } 

ExpandableList.java

 package com.example; import android.app.Activity; import android.graphics.Color; import android.os.Bundle; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.LayoutParams; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListAdapter; import android.widget.SectionIndexer; import android.widget.TextView; import android.widget.Toast; import com.example.PinnedHeaderExpListView.PinnedHeaderAdapter; /** * Demonstrates expandable lists using a custom {@link ExpandableListAdapter} * from {@link BaseExpandableListAdapter}. */ public class ExpandableList extends Activity { MyExpandableListAdapter mAdapter; PinnedHeaderExpListView elv; private int mPinnedHeaderBackgroundColor; private int mPinnedHeaderTextColor; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Set up our adapter mAdapter = new MyExpandableListAdapter(); elv = (PinnedHeaderExpListView) findViewById(R.id.list); elv.setAdapter(mAdapter); mPinnedHeaderBackgroundColor = getResources().getColor(android.R.color.black); mPinnedHeaderTextColor = getResources().getColor(android.R.color.white); elv.setGroupIndicator(null); View h = LayoutInflater.from(this).inflate(R.layout.header, (ViewGroup) findViewById(R.id.root), false); elv.setPinnedHeaderView(h); elv.setOnScrollListener((OnScrollListener) mAdapter); elv.setDividerHeight(0); } /** * A simple adapter which maintains an ArrayList of photo resource Ids. * Each photo is displayed as an image. This adapter supports clearing the * list of photos and adding a new photo. * */ public class MyExpandableListAdapter extends BaseExpandableListAdapter implements PinnedHeaderAdapter, OnScrollListener{ // Sample data set. children[i] contains the children (String[]) for groups[i]. private String[] groups = { "People Names", "Dog Names", "Cat Names", "Fish Names" }; private String[][] children = { { "Arnold", "Barry", "Chuck", "David", "Stas", "Oleg", "Max","Alex","Romeo", "Adolf" }, { "Ace", "Bandit", "Cha-Cha", "Deuce", "Nokki", "Baron", "Sharik", "Toshka","SObaka","Belka","Strelka","Zhuchka"}, { "Fluffy", "Snuggles","Cate", "Yasha","Bars" }, { "Goldy", "Bubbles","Fluffy", "Snuggles","Guffy", "Snoopy" } }; public Object getChild(int groupPosition, int childPosition) { return children[groupPosition][childPosition]; } public long getChildId(int groupPosition, int childPosition) { return childPosition; } public int getChildrenCount(int groupPosition) { return children[groupPosition].length; } public TextView getGenericView() { // Layout parameters for the ExpandableListView AbsListView.LayoutParams lp = new AbsListView.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 64); TextView textView = new TextView(ExpandableList.this); textView.setLayoutParams(lp); // Center the text vertically textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); // Set the text starting position textView.setPadding(36, 0, 0, 0); return textView; } public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { TextView textView = getGenericView(); textView.setText(getChild(groupPosition, childPosition).toString()); return textView; } public Object getGroup(int groupPosition) { return groups[groupPosition]; } public int getGroupCount() { return groups.length; } public long getGroupId(int groupPosition) { return groupPosition; } public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { TextView textView = (TextView) LayoutInflater.from(getApplicationContext()).inflate(R.layout.header, parent, false); textView.setText(getGroup(groupPosition).toString()); return textView; } public boolean isChildSelectable(int groupPosition, int childPosition) { return true; } public boolean hasStableIds() { return true; } /** * размытие/пропадание хэдера */ public void configurePinnedHeader(View v, int position, int alpha) { TextView header = (TextView) v; final String title = (String) getGroup(position); header.setText(title); if (alpha == 255) { header.setBackgroundColor(mPinnedHeaderBackgroundColor); header.setTextColor(mPinnedHeaderTextColor); } else { header.setBackgroundColor(Color.argb(alpha, Color.red(mPinnedHeaderBackgroundColor), Color.green(mPinnedHeaderBackgroundColor), Color.blue(mPinnedHeaderBackgroundColor))); header.setTextColor(Color.argb(alpha, Color.red(mPinnedHeaderTextColor), Color.green(mPinnedHeaderTextColor), Color.blue(mPinnedHeaderTextColor))); } } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (view instanceof PinnedHeaderExpListView) { ((PinnedHeaderExpListView) view).configureHeaderView(firstVisibleItem); } } public void onScrollStateChanged(AbsListView view, int scrollState) { // TODO Auto-generated method stub } } } 

main.xml中

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" > <view class="com.example.PinnedHeaderExpListView" android:id="@+id/list" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> 

header.xml

 <?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/header" android:layout_width="match_parent" android:layout_height="25dp" android:background="@android:color/black" > </TextView> 

它按预期工作,除了点击标题。 我希望点击标题将等于点击组项目,但事件甚至不会发生,OnClickListener不获得控制。 为什么?

编辑:单击标题也可以工作,如果您在onCreate()方法ExpandableList.java活动内添加以下一段代码。

  elv.setOnGroupClickListener(new OnGroupClickListener() { @Override public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) { return false; } }); 

我试图实现人隐身的解决scheme,并遇到固定头不可点击相同的问题。

罪魁祸首似乎是ListView本身消耗所有的点击事件,因此不会传递给我们的'扩充'标题视图。 所以你可以尝试在ExpandableListView的点击处理实现中挖掘,考虑到inheritance一直到AdapterView,这是相当混乱的。

而不是尝试从ListView劫持点击,我尝试通过模拟下面的列表项中的标题点击来环绕这样的问题。 为此,您需要实现onChildClick来首先查看被单击项目的位置是否位于固定标题下方,如果是,则可以将点击中继到真正标题,如果不是,则只处理点击项目正常。

在下面的例子中,当被点击的项目位于固定标题下方时,我只是简单地将ListView滚动到真正的标题,从而用真正的标题replace“增强的”固定标题,用户可以从那里采取进一步的行动,例如崩溃的小组。

请注意,这样的可用性stream程只有在没有任何可点击的标题项目视图的情况下才能起作用,否则您将必须使用onInterceptTouchEvent固定的虚拟标题和真正的标题之间的所有点击中继和映射。

在Homo PinnedHeaderExpListView.java代码之后,在PinnedHeaderExpListView.java添加以下方法:

 public int getHeaderViewHeight(){ return mHeaderViewHeight; } 

onCreate活动中,追加以下内容:

 elv.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { // we need to obtain the relative y coordinate of the child view, // not its clicked subview, thus first we try to calculate its true index long packedPos = ExpandableListView.getPackedPositionForChild(groupPosition, childPosition); int viewPos = elv.getFlatListPosition(packedPos) - elv.getFirstVisiblePosition(); View childView = parent.getChildAt(viewPos); // got it if (childView.getTop() < elv.getHeaderViewHeight()*.75){ // if the clicked child item overlaps more than 25% // of pinned header, consider it being underneath long groupPackedPos = ExpandableListView.getPackedPositionForGroup(groupPosition); int groupFlatPos = elv.getFlatListPosition(groupPackedPos); elv.smoothScrollToPosition(groupFlatPos); } return true; } }); 

在Homo Incognito的回答中,固定头部视图中的子视图不能点击并接收点击事件,但是我find了一个方法。 我把代码放在: https : //github.com/chenjishi/PinnedHeadListView

 private final Rect mRect = new Rect(); private final int[] mLocation = new int[2]; @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mHeaderView == null) return super.dispatchTouchEvent(ev); if (mHeaderViewVisible) { final int x = (int) ev.getX(); final int y = (int) ev.getY(); mHeaderView.getLocationOnScreen(mLocation); mRect.left = mLocation[0]; mRect.top = mLocation[1]; mRect.right = mLocation[0] + mHeaderView.getWidth(); mRect.bottom = mLocation[1] + mHeaderView.getHeight(); if (mRect.contains(x, y)) { if (ev.getAction() == MotionEvent.ACTION_UP) { performViewClick(x, y); } return true; } else { return super.dispatchTouchEvent(ev); } } else { return super.dispatchTouchEvent(ev); } } private void performViewClick(int x, int y) { if (null == mHeaderView) return; final ViewGroup container = (ViewGroup) mHeaderView; for (int i = 0; i < container.getChildCount(); i++) { View view = container.getChildAt(i); /** * transform coordinate to find the child view we clicked * getGlobalVisibleRect used for android 2.x, getLocalVisibleRect * user for 3.x or above, maybe it's a bug */ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { view.getGlobalVisibleRect(mRect); } else { view.getLocalVisibleRect(mRect); int width = mRect.right - mRect.left; mRect.left = Math.abs(mRect.left); mRect.right = mRect.left + width; } if (mRect.contains(x, y)) { view.performClick(); break; } } } 

这是我处理固定视图中的单击事件,重写dispatchTouchEvent。

在这里输入图像说明

在适配器中:

 public void setSelectedPosition(int position){ this.listChildPosition=position; } 

在getchildview

  if (listChildPosition == childPosition) { convertView.setBackgroundColor(context.getResources().getColor( R.color.white)); } else { convertView.setBackgroundColor(context.getResources().getColor( R.color.expandlist)); } 

在onChildClick

  adapter.setSelectedPosition(childPosition); adapter.notifyDataSetChanged(); v.setSelected(true);