vertical studio sirve que para pantalla linearlayout hacia funciona deslizar como codepath abajo android android-listview android-scrollview

android - studio - Haciendo un elemento medio para atascarse en el encabezado(ScrollView/ListView)



scrollview linearlayout (3)

Quiero hacer un elemento que se muestra en el medio de ScrollView (o ListView ) en el primer momento y luego se queda atascado en el encabezado de la pantalla cuando se desplaza.

Es una implementación prototipo en CSS + JS: http://jsfiddle.net/minhee/aPcv4/embedded/result/ .

A primera vista haría ScrollView para incluir ListView , pero los documentos oficiales dicen:

Nunca debe usar un ScrollView con un ListView, porque ListView se ocupa de su propio desplazamiento vertical. Lo más importante es que hacer esto anula todas las optimizaciones importantes en ListView para tratar con listas grandes, ya que obliga a ListView a mostrar su lista completa de elementos para llenar el contenedor infinito proporcionado por ScrollView.

Entonces, ¿qué enfoques puedo tratar de lograr esta interfaz de usuario?

Actualización : probé StickyListHeaders , pero: "actualmente no es posible tener elementos interactivos en el encabezado, los botones, los interruptores, etc. solo funcionarán cuando el encabezado no esté atascado". Además, creo que no es muy adecuado para esta situación . No necesito varios encabezados, sino solo un elemento central para bloquearse en el encabezado.


Este es el código más simple para ilustrar la idea principal:

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) {} });

Tengo un FrameLayout que no contiene nada más que mi 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 es la posición de su elemento (por ejemplo, PINNED_ITEM = 2 ). Este diseño actúa como una superposición para la lista. El ScrollListener seguimiento de los elementos visibles actuales y, si detecta que un elemento debe fijarse, lo agrega al diseño y lo elimina de lo contrario.

La línea pinnedView.setBackgroundColor(0xFF000000); es necesario para establecer un fondo opaco para el elemento. El elemento será transparente si no haces esto. Puede ajustar el fondo según sus necesidades (por ejemplo, puede utilizar un fondo de su atributo de tema actual).


He usado (o más bien, he tratado de usar) la biblioteca StickyListHeaders en el pasado. Después de tener algunos problemas con esto, se me ocurrió lo siguiente. No es muy diferente de lo que otros carteles han sugerido.

El archivo de diseño principal activity_layout.xml consiste en un ListView y un LinearLayout que es invisible por defecto. Al usar el método onScroll () de LinearLayout''s se cambia LinearLayout''s visibilidad LinearLayout''s . No es necesario inflar otro diseño o agregar vistas dinámicamente al elemento principal de su diseño. Así es como se ve el método 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); } }

Simplemente, alterne la visibilidad para obtener el efecto deseado. Puedes echar un vistazo al siguiente código. Es un ejemplo de trabajo de lo que puede esperar. La actividad contiene un ListView con una extensión estrictamente BaseAdapter de BaseAdapter . El ListView se llena con botones numerados (uno en cada fila, comenzando en 0 y subiendo hasta 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>


Para lograr esto, pondré el listView en un relativeLayout y agregaré un oyente en el desplazamiento. Luego, en él, cuando cambie el primer objeto visible, duplicaré el itemView que necesita y lo mostraré en su listView.

Aquí hay una breve muestra de lo que pienso. Lo único es que la vista duplicada tiene que ser opaca.

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); } } }); } }

Y 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" />