discretescrollview - image slider android studio
¿Cómo crear una página desplazable de carruseles en Android? (6)
Idea principal
Para tener un diseño flexible y tener elementos ilimitados , puede crear un RecyclerView
como vista de raíz con LinearLayoutManager.VERTICAL
como LayoutManager
. para cada fila puede poner otra RecyclerView
pero ahora con LinearLayoutManager.HORIZONTAL
como LayoutManager
.
Resultado
Fuente
Requisitos
1) El desplazamiento vertical entre carruseles debe ser uniforme, pero cuando el usuario lo suelta, la IU debe "ajustarse" al carrusel más cercano (para que el usuario esté siempre en una fila de carrusel, no entre dos carruseles).
2) El desplazamiento horizontal en un carrusel debe ser uniforme, pero cuando el usuario lo suelta, la IU debe "ajustarse" al elemento más cercano en el carrusel.
Para lograr ésos utilicé OnScrollListener
y cuando los estados van SCROLL_STATE_IDLE miro las vistas superior e inferior para ver cuál de ellas tiene una región más visible y luego me desplazo a esa posición. para cada fila, lo hago para las vistas izquierda y derecha de cada adaptador de fila. De esta forma, siempre quedará un lado de tus carruseles o filas. por ejemplo, si la parte superior está ajustada, la parte inferior no es o viceversa. Creo que si juegas un poco más puedes lograr eso, pero debes conocer la dimensión de la ventana y cambiar la dimensión de los carruseles en tiempo de ejecución.
3) Debería ser posible superponer información adicional sobre un elemento en el carrusel
Si utiliza RelativeLayout
o FrameLayout
como una vista de raíz de cada elemento, puede poner información uno encima del otro. como puede ver, los números están en la parte superior de las imágenes.
4) La interfaz de usuario debe ser adaptable a cualquier tamaño de pantalla.
si sabe cómo admitir el tamaño de pantalla múltiple puede hacerlo fácilmente, si no sabe leer el documento. Compatible con pantallas múltiples
5) Debe ser navegable con las teclas de flecha (para dispositivos sin pantalla táctil)
usar debajo de la función
mRecyclerView.scrollToPosition(position);
6) Debería funcionar en una amplia gama de versiones de Android (posiblemente a través de la biblioteca de soporte)
Importar Android. support.v7 .widget.RecyclerView;
7) Debe estar bien para usar en una aplicación de código abierto con licencia bajo la GPL
De acuerdo
feliz codificación !!
Estoy intentando construir una interfaz de usuario para mi aplicación de Android que contiene una página verticalmente desplazable de carruseles desplazables horizontalmente (algo así como lo que hace la aplicación Netflix). ¿Cómo se logra este tipo de comportamiento?
Una implementación básica sería suficiente para comenzar. Existen algunos otros requisitos para la IU, que incluiré aquí como referencia, ya que pueden afectar las clases o bibliotecas que puedo usar.
1) El desplazamiento vertical entre carruseles debe ser uniforme, pero cuando el usuario lo suelta, la IU debe "ajustarse" al carrusel más cercano (para que el usuario esté siempre en una fila de carrusel, no entre dos carruseles).
2) El desplazamiento horizontal en un carrusel debe ser uniforme, pero cuando el usuario lo suelta, la IU debe "ajustarse" al elemento más cercano en el carrusel.
3) Debería ser posible superponer información adicional sobre un elemento en el carrusel
4) La interfaz de usuario debe ser adaptable a cualquier tamaño de pantalla.
5) Debe ser navegable con las teclas de flecha (para dispositivos sin pantalla táctil)
6) Debería funcionar en una amplia gama de versiones de Android (posiblemente a través de la biblioteca de soporte)
7) Debe estar bien para usar en una aplicación de código abierto con licencia bajo la GPL
Las respuestas aceptables NO tienen que cumplir con todos estos requisitos. Como mínimo, una buena respuesta debería involucrar la navegación de carruseles múltiples (frente a un solo carrusel).
Aquí hay una maqueta de básicamente lo que estoy visualizando (soy flexible, no tiene que parecerse a esto ... el punto es solo aclarar a qué me refiero; cada fila contendría una gran cantidad de elementos que podrían desplazarse hacia la izquierda y hacia la derecha, y toda la página se puede desplazar hacia arriba y hacia abajo)
Hace un tiempo necesitaba algo así, acabo de usar eso: https://github.com/simonrob/Android-Horizontal-ListView
Simple, poderoso, personalizable.
Ejemplo de mi versión:
public class HorizontalListView extends AdapterView<ListAdapter> {
public boolean mAlwaysOverrideTouch = true;
protected ListAdapter mAdapter;
private int mLeftViewIndex = -1;
private int mRightViewIndex = 0;
protected int mCurrentX;
protected int mNextX;
private int mMaxX = Integer.MAX_VALUE;
private int mDisplayOffset = 0;
protected Scroller mScroller;
private GestureDetector mGesture;
private Queue<View> mRemovedViewQueue = new LinkedList<View>();
private OnItemSelectedListener mOnItemSelected;
private OnItemClickListener mOnItemClicked;
private OnItemLongClickListener mOnItemLongClicked;
private boolean mDataChanged = false;
public HorizontalListView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
private synchronized void initView() {
mLeftViewIndex = -1;
mRightViewIndex = 0;
mDisplayOffset = 0;
mCurrentX = 0;
mNextX = 0;
mMaxX = Integer.MAX_VALUE;
mScroller = new Scroller(getContext());
mGesture = new GestureDetector(getContext(), mOnGesture);
}
@Override
public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {
mOnItemSelected = listener;
}
@Override
public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
mOnItemClicked = listener;
}
@Override
public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {
mOnItemLongClicked = listener;
}
private DataSetObserver mDataObserver = new DataSetObserver() {
@Override
public void onChanged() {
synchronized (HorizontalListView.this) {
mDataChanged = true;
}
invalidate();
requestLayout();
}
@Override
public void onInvalidated() {
reset();
invalidate();
requestLayout();
}
};
@Override
public ListAdapter getAdapter() {
return mAdapter;
}
@Override
public View getSelectedView() {
//TODO: implement
return null;
}
@Override
public void setAdapter(ListAdapter adapter) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(mDataObserver);
}
mAdapter = adapter;
mAdapter.registerDataSetObserver(mDataObserver);
reset();
}
private synchronized void reset() {
initView();
removeAllViewsInLayout();
requestLayout();
}
@Override
public void setSelection(int position) {
//TODO: implement
}
private void addAndMeasureChild(final View child, int viewPos) {
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
}
addViewInLayout(child, viewPos, params, true);
child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
}
@Override
protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mAdapter == null) {
return;
}
if (mDataChanged) {
int oldCurrentX = mCurrentX;
initView();
removeAllViewsInLayout();
mNextX = oldCurrentX;
mDataChanged = false;
}
if (mScroller.computeScrollOffset()) {
mNextX = mScroller.getCurrX();
}
if (mNextX <= 0) {
mNextX = 0;
mScroller.forceFinished(true);
}
if (mNextX >= mMaxX) {
mNextX = mMaxX;
mScroller.forceFinished(true);
}
int dx = mCurrentX - mNextX;
removeNonVisibleItems(dx);
fillList(dx);
positionItems(dx);
mCurrentX = mNextX;
if (!mScroller.isFinished()) {
post(new Runnable() {
@Override
public void run() {
requestLayout();
}
});
}
}
private void fillList(final int dx) {
int edge = 0;
View child = getChildAt(getChildCount() - 1);
if (child != null) {
edge = child.getRight();
}
fillListRight(edge, dx);
edge = 0;
child = getChildAt(0);
if (child != null) {
edge = child.getLeft();
}
fillListLeft(edge, dx);
}
private void fillListRight(int rightEdge, final int dx) {
while (rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {
View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, -1);
rightEdge += child.getMeasuredWidth();
if (mRightViewIndex == mAdapter.getCount() - 1) {
mMaxX = mCurrentX + rightEdge - getWidth();
}
if (mMaxX < 0) {
mMaxX = 0;
}
mRightViewIndex++;
}
}
private void fillListLeft(int leftEdge, final int dx) {
while (leftEdge + dx > 0 && mLeftViewIndex >= 0) {
View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);
addAndMeasureChild(child, 0);
leftEdge -= child.getMeasuredWidth();
mLeftViewIndex--;
mDisplayOffset -= child.getMeasuredWidth();
}
}
private void removeNonVisibleItems(final int dx) {
View child = getChildAt(0);
while (child != null && child.getRight() + dx <= 0) {
mDisplayOffset += child.getMeasuredWidth();
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mLeftViewIndex++;
child = getChildAt(0);
}
child = getChildAt(getChildCount() - 1);
while (child != null && child.getLeft() + dx >= getWidth()) {
mRemovedViewQueue.offer(child);
removeViewInLayout(child);
mRightViewIndex--;
child = getChildAt(getChildCount() - 1);
}
}
private void positionItems(final int dx) {
if (getChildCount() > 0) {
mDisplayOffset += dx;
int left = mDisplayOffset;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
int childWidth = child.getMeasuredWidth();
child.layout(left, 0, left + childWidth, child.getMeasuredHeight());
left += childWidth;
}
}
}
public synchronized void scrollTo(int x) {
mScroller.startScroll(mNextX, 0, x - mNextX, 0);
requestLayout();
}
public synchronized void scrollToChild(int position) {
//TODO
requestLayout();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return mGesture.onTouchEvent(ev);
}
protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
synchronized (HorizontalListView.this) {
mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0);
}
requestLayout();
return true;
}
protected boolean onDown(MotionEvent e) {
mScroller.forceFinished(true);
return true;
}
private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return HorizontalListView.this.onDown(e);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
synchronized (HorizontalListView.this) {
mNextX += (int) distanceX;
}
requestLayout();
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Rect viewRect = new Rect();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
int left = child.getLeft();
int right = child.getRight();
int top = child.getTop();
int bottom = child.getBottom();
viewRect.set(left, top, right, bottom);
if (viewRect.contains((int) e.getX(), (int) e.getY())) {
if (mOnItemClicked != null) {
mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));
}
if (mOnItemSelected != null) {
mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));
}
break;
}
}
return true;
}
@Override
public void onLongPress(MotionEvent e) {
Rect viewRect = new Rect();
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
int left = child.getLeft();
int right = child.getRight();
int top = child.getTop();
int bottom = child.getBottom();
viewRect.set(left, top, right, bottom);
if (viewRect.contains((int) e.getX(), (int) e.getY())) {
if (mOnItemLongClicked != null) {
mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));
}
break;
}
}
}
};
}
Aquí está el XML:
<com.example.package.widgets.HorizontalListView
android:id="@+id/horizontal_listview"
android:layout_marginTop="30dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_width="fill_parent"
android:layout_height="80dp"
android:background="@color/light_gray"
/>
En el OnCreate:
mAdapter = new ArrayAdapter<Uri>(this, R.layout.viewitem) {
@Override
public int getCount() {
return listUriAdapter.size();
}
@Override
public Uri getItem(int position) {
return listUriAdapter.get(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
// do what you have to do
return retval;
}
};
onItemClickListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
}
};
onItemLongClickListener = new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
return false;
}
};
horizontalListView.setOnItemClickListener(onItemClickListener);
horizontalListView.setOnItemLongClickListener(onItemLongClickListener);
horizontalListView.setAdapter(mAdapter);
Prueba ViewPager. This es la documentación.
Puede usar ListView con un OnTouchListener personalizado (para ajustar elementos) para el desplazamiento vertical y TwoWayGridView nuevamente con un OnTouchListener personalizado (para ajustar elementos)
main.xml
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/containerList"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#E8E8E8"
android:divider="@android:color/transparent"
android:dividerHeight="16dp" />
list_item_hgrid.xml
<com.jess.ui.TwoWayGridView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/grid"
android:layout_width="match_parent"
android:layout_height="160dp"
android:layout_marginBottom="16dp"
app:cacheColorHint="#E8E8E8"
app:columnWidth="128dp"
app:gravity="center"
app:horizontalSpacing="16dp"
app:numColumns="auto_fit"
app:numRows="1"
app:rowHeight="128dp"
app:scrollDirectionLandscape="horizontal"
app:scrollDirectionPortrait="horizontal"
app:stretchMode="spacingWidthUniform"
app:verticalSpacing="16dp" />
Y el código de actividad será algo así como el siguiente
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
ListView containerList = (ListView) findViewById(R.id.containerList);
containerList.setAdapter(new DummyGridsAdapter(this));
containerList.setOnTouchListener(mContainerListOnTouchListener);
}
private View.OnTouchListener mContainerListOnTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
View itemView = ((ListView) view).getChildAt(0);
int top = itemView.getTop();
if (Math.abs(top) >= itemView.getHeight() / 2) {
top = itemView.getHeight() - Math.abs(top);
}
((ListView) view).smoothScrollBy(top, 400);
}
return false;
}
};
Y aquí están los adaptadores de prueba
private static class DummyGridsAdapter extends BaseAdapter {
private Context mContext;
private TwoWayGridView[] mChildGrid;
public DummyGridsAdapter(Context context) {
mContext = context;
mChildGrid = new TwoWayGridView[getCount()];
for (int i = 0; i < mChildGrid.length; i++) {
mChildGrid[i] = (TwoWayGridView) LayoutInflater.from(context).
inflate(R.layout.list_item_hgrid, null);
mChildGrid[i].setAdapter(new DummyImageAdapter(context));
mChildGrid[i].setOnTouchListener(mChildGridOnTouchListener);
}
}
@Override
public int getCount() {
return 8;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return mChildGrid[position];
}
private View.OnTouchListener mChildGridOnTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
View itemView = ((TwoWayGridView) view).getChildAt(0);
int left = itemView.getLeft();
if (Math.abs(left) >= itemView.getWidth() / 2) {
left = itemView.getWidth() - Math.abs(left);
}
((TwoWayGridView) view).smoothScrollBy(left, 400);
}
return false;
}
};
}
private static class DummyImageAdapter extends BaseAdapter {
private Context mContext;
private final int mDummyViewWidthHeight;
public DummyImageAdapter(Context context) {
mContext = context;
mDummyViewWidthHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 128,
context.getResources().getDisplayMetrics());
}
@Override
public int getCount() {
return 16;
}
@Override
public Object getItem(int position) {
int component = (getCount() - position - 1) * 255 / getCount();
return Color.argb(255, 255, component, component);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView = new ImageView(mContext);
imageView.setBackgroundColor((Integer) getItem(position));
imageView.setLayoutParams(new TwoWayGridView.LayoutParams(mDummyViewWidthHeight, mDummyViewWidthHeight));
return imageView;
}
}
Puede usar un ScrollView
como padre dentro de ese ScrollView
coloque Vertical LinearLayout
in for loop
inflar un diseño que consiste en coverflow
para efecto carrusel
Sugeriría la vista de Recycler.
Puede crear listas horizontales y verticales o vistas de cuadrícula. En mi opinión, el viewpager puede complicarse a veces.
Estoy trabajando en la aplicación de video a pedido y esto me salvó.
En tu caso, será fácil de configurar. Te daré un código.
Necesitarás lo siguiente:
Vista XML: donde se declara el diseño de reciclaje.
Adaptador: necesitará una vista para completar el adaptador y completar la vista de reciclaje.
Creando la vista
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:orientation="horizontal"
android:gravity="center"
android:overScrollMode="never"/>
Declare esto donde desea que se muestre el carrusel.
A continuación, quiere crear el adaptador:
public class HorizontalCarouselItemAdapter extends RecyclerView.Adapter<HorizontalCarouselItemAdapter.ViewHolder> {
List<objects> items;
int itemLayout;
public HorizontalCarouselItemAdapter(Context context, int itemLayout, List<objects> items) {
this.context = context;
this.itemLayout = itemLayout;
this.items = items;
}
@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(itemLayout, parent, false);
return new ViewHolder(v);
}
@Override public void onBindViewHolder(final ViewHolder holder, final int position) {
this.holders = holder;
final GenericAsset itemAdapter = items.get(position);
holder.itemImage.setDrawable //manipulate variables here
}
@Override public int getItemCount() {
return items.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView itemImage;
public ViewHolder(View itemView) {
super(itemView);
itemImage = (ImageView) itemView.findViewById(R.id.carousel_cell_holder_image);
}
}
Aquí es donde usted alimenta los datos al adaptador para llenar cada elemento del carrusel.
Finalmente declararlo y llamar al adaptador:
recyclerView = (RecyclerView)findViewById(R.id.recycle_view);
ListLayoutManager manager = new ListLayoutManager(getApplication(), ListLayoutManager.Orientation.HORIZONTAL);
recyclerView.setLayoutManager(manager);
CustomAdpater adapter = new CustomAdapter(getApplication(), data);
recyclerView.setAdapter(adapter);
Puede crear una vista de lista con vistas de reciclaje para lograr lo que desea.
Esta clase es ideal para el desplazamiento suave y la optimización de la memoria.
Este es el enlace para ello:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
Espero que esto te ayude.