Hay una forma estándar de anclar elemento de grupo en la parte superior de la pantalla mientras se desplazan los elementos del grupo. Vi ejemplos similares con ListView. ¿Qué interfaces debo implementar o qué métodos anulan?Grupos fijados en ExpandableListView
Respuesta
He encontrado solución basada en Pinned Header ListView de Pedro Kuterna y ExpandableList1.java muestra androide. 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>
Funciona como se esperaba, excepto al hacer clic en encabezamiento. Deseo que al hacer clic en el encabezado sea igual al hacer clic en el elemento del grupo, pero el evento ni siquiera ocurre y OnClickListener no obtiene el control. Por qué ?
EDIT: clic en la cabecera también funciona si se agrega siguiente fragmento de código dentro de onCreate()
método de ExpandableList.java
actividad.
elv.setOnGroupClickListener(new OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
return false;
}
});
de adaptador:
public void setSelectedPosition(int position){
this.listChildPosition=position;
}
en getchildview
if (listChildPosition == childPosition) {
convertView.setBackgroundColor(context.getResources().getColor(
R.color.white));
} else {
convertView.setBackgroundColor(context.getResources().getColor(
R.color.expandlist));
}
en onChildClick
adapter.setSelectedPosition(childPosition);
adapter.notifyDataSetChanged();
v.setSelected(true);
esto no es, lo que pedí, en absoluto –
He tratado de implementar la solución de Homo Incognito y encuentro el mismo problema de que no se pueda hacer clic en el encabezado fijado.
El culpable parece ser el ListView consumiendo todo el evento de clic, por lo tanto, no pasa nada a nuestra vista de encabezado 'aumentada'. Por lo tanto, podría intentar profundizar en la implementación del manejo de clics de ExpandableListView, lo cual es bastante complicado teniendo en cuenta su herencia hasta llegar a AdapterView.
En lugar de intentar interceptar el clic de ListView, intento circunnavegar dicho problema simulando el clic del encabezado del elemento de la lista debajo de él. Para hacerlo, debe implementar el onChildClick
para ver primero si la posición del elemento cliqueado se encuentra debajo del encabezado fijado, si es así, puede retransmitir el clic al encabezado verdadero, de lo contrario, simplemente procese el clic para ese artículo normalmente
En el siguiente ejemplo, cuando el artículo se hace clic está debajo de la cabecera de clavado, simplemente hizo la ListView desplazamiento a la verdadera cabeza, sustituyendo así el 'aumentada' cabecera cubrió con la verdadera cabeza, y el usuario puede tomar medidas adicionales desde allí, por ejemplo colapsando el grupo.
Tenga en cuenta que tal flujo usabilidad sólo funciona si usted no tiene ningún punto de vista se puede hacer clic en los elementos de cabecera, de lo contrario tendrá que hacer todo el relé clic y mapeo entre la cabecera virtual de clavado y el verdadero encabezado usted mismo con onInterceptTouchEvent
.
siguiente código de Homo Incognito, en PinnedHeaderExpListView.java
añade el siguiente método:
public int getHeaderViewHeight(){
return mHeaderViewHeight;
}
en la actividad onCreate
, añada lo siguiente:
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;
}
});
en la respuesta de Homo Incognito, vista niño en la vista frontal fija no puede hacer clic y recibir el evento de clic, pero encontré la manera. Pongo el código en: 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;
}
}
}
esta es la forma en que manejo el evento click en vista clavada, anular dispatchTouchEvent.
- 1. ¿Cómo obtener Encabezados fijos/fijados en una ExpandableListView?
- 2. ExpandableListView muestra el indicador para grupos sin hijos
- 3. ExpandableListView dentro de ExpandableListView
- 4. ExpandableListView Group View Expandir Predeterminado
- 5. de varias capas ExpandableListView
- 6. Android LongClickListener en elementos del grupo ExpandableListView
- 7. cómo ocultar el indicador ExpandableListView para los grupos que no tienen hijos
- 8. Casilla de verificación en ExpandableListView
- 9. Efecto de deslizamiento en ExpandableListView
- 10. Definición de ExpandableListView en xml
- 11. SimpleCursorTreeAdapter y CursorLoader para ExpandableListView
- 12. ExpandableListView con un EditText
- 13. muestra ExpandableListView de Google
- 14. ExpandableListView y casillas de verificación
- 15. ExpandableListView Actualizando con notifyDataSetChanged()
- 16. Android ExpandableListView posición de grupo siempre 0
- 17. Colapsar mediante programación un grupo en ExpandableListView
- 18. Android: Ocultar divisores secundarios en ExpandableListView
- 19. ¿Cómo implementar Multinivel ExpandableListview en Android?
- 20. ¿Es ChoiceMode compatible con ExpandableListView?
- 21. Android ExpandableListView - Buscando un tutorial
- 22. Android ExpandableListView padre con botón
- 23. Android ExpandableListView divisor es invisible
- 24. ExpandableListView - ¿Es posible la expansión condicional?
- 25. Indicador/ícono de Android ExpandableListView siempre extendido
- 26. ¿Cómo puedo eliminar el botón de ExpandableListView?
- 27. ExpandableListView contraer todos los elementos principales
- 28. ListView/ExpandableListView setEmptyView() no tiene efecto
- 29. Ver los contenidos a continuación ExpandableListView
- 30. Android ExpandableListView y base de datos SQLite
¿De dónde obtiene sus secciones de? Porque group es el elemento de la lista con los niños. No veo en ninguna parte donde estableces los encabezados y las secciones. ¿Podría agregar capturas de pantalla para saber qué esperar de este código? ¿Cómo se configuran las secciones? En configurePinnedHeader, crea un encabezado, pero solo se llama una vez porque no sé cómo crear las secciones que van debajo de los encabezados. He leído la respuesta muchas veces y parece que no puedo resolverlo. ¿Ayuda por favor? –
Esto es muy bueno ... gracias :) por que podemos hacer que los encabezados de grupo estén siempre visibles ... en su forma actual, solo el encabezado activo actual queda anclado si el número de elementos asociados con él es grande y el otros encabezados son expulsados de la región de visualización – Nav
Hola @Homo ¿hay alguna forma de agregar animación para expandir/contraer operación? – Tony