2012-05-16 16 views

回答

15

我發現基於彼得Kuterna和Android樣品ExpandableList1.java的Pinned Header ListView溶液。 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不能控制。爲什麼?

編輯:如果您添加下面的代碼段內ExpandableList.java活動onCreate()方法 點擊標題上也適用。

 elv.setOnGroupClickListener(new OnGroupClickListener() { 

     @Override 
     public boolean onGroupClick(ExpandableListView parent, View v, 
       int groupPosition, long id) { 
      return false; 
     } 
    }); 
+0

你在哪裏得到你的部分?因爲組是兒童的列表項。我沒有看到你設置標題和部分的地方。你可以添加截圖,以便我可以知道該代碼期望什麼?你如何設置部分?在configurePinnedHeader中,它會生成一個頭文件,但這隻會被調用一次,因爲我不知道如何創建頭文件下的部分。我已經多次閱讀了答案,我似乎無法弄清楚。請幫助? –

+0

這是非常好的......謝謝:)通過我們可以使組標題始終可見...在其當前形式中,只有當前活動標題被固定,如果與它關聯的項目數很大,其他標題被推出觀看區域 – Nav

+0

嘿@霍莫有什麼辦法來添加動畫來展開/合攏操作? – Tony

-3

在適配器:

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); 
+0

這不,我問了,在所有 –

3

我試圖實現Homo隱身的解決方案,並遇到固定頭不可點擊的相同問題。

罪魁禍首似乎是ListView本身消耗所有的點擊事件,因此不會傳遞給我們的'增強'標題視圖。所以你可以嘗試在ExpandableListView的點擊處理實現中進行挖掘,考慮到繼承一直到AdapterView,這是相當混亂的。

我試圖通過模擬從它下面的列表項目模擬標題單擊來繞過這樣的問題,而不是嘗試從ListView中劫持點擊。爲此,您需要實現onChildClick以首先查看被單擊項目的位置是否位於固定標題下方,如果是,則可以將點擊繼續傳遞到真正標題,如果不是,則只需處理點擊項目正常。



在下面的例子中,當點擊項目是被釘扎頭的下面,我只是使ListView中滾動到真標題,從而取代了「增強」釘扎頭與真實標題,用戶可以從那裏採取進一步行動,例如破壞組織。

注意,這種使用流只適用,如果你沒有在標題項目的任何點擊視圖,否則你將不得不做的固定虛擬頭和真正的頭一下onInterceptTouchEvent之間的所有點擊繼電器和製圖。


繼智人微服私訪的代碼,在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; 
     } 
    }); 
0

在智人微服私訪的回答,子視圖固定的頭部視圖不能點擊並接收點擊事件,但我找到了一種方法。我把代碼爲: 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。

enter image description here