ListView circular (elementos en el medio círculo)
Estoy tratando de hacer un ListView circular con elementos de lista dispuestos en medio círculo. Debe verse algo como esto:
- Creación de un ListView con elementos de lista personalizados mediante programación en Android - no hay ningún elemento de lista de elementos XML
- Cuadro de diálogo de alerta personalizado con listview personalizado
- ¿Cómo puedo obtener mi ListView para desplazarse?
- Cómo deshabilitar el elemento ListView después de que se ha hecho clic?
- Posición incorrecta de elementos después de usar un customAdapter
Había un post relacionado pero estaba cerrado.
Hice mi propio ListView personalizado circular y funciona bien, pero mi problema es que no puedo arreglar los elementos de lista de medio círculo como se muestra en la imagen. Intenté varias cosas pero fue inútil, no sé cómo hacerlo.
- ¿Cómo hacer que los elementos se puedan hacer clic en la vista de lista?
- Establecer OnClick Listener en el botón dentro de la vista de lista en android
- Agregar un separador de fecha para un SimpleCursorAdapter en Android
- Cómo guardar y restaurar la posición de ListView en Android
- ListView en ArrayAdapter orden se mezcla cuando se desplaza
- Android - ListView extensible SetIndicatorBounds no funcionan en Kitkat android-4.4
- Android: separador personalizado (o incluso elemento) en ListView depening en el contenido del elemento
- Agregar la barra de progreso en la parte inferior de una vista de lista
Así que cuando hice la aplicación de ejemplo para demostración de esto tuve que hacer 2 cosas.
En primer lugar, editar el onDraw(Canvas)
en mi vista personalizada. Esto podría ser cualquier vista, lo hago un TextView para la simplicidad. Esto me permite empujar la vista sobre la base de una ecuación.
public class MyView extends TextView { private static final int MAX_INDENT = 300; private static final String TAG = MyView.class.getSimpleName(); public MyView(Context context) { super(context); } public void onDraw(Canvas canvas){ canvas.save(); float indent = getIndent(getY()); canvas.translate(indent, 0); super.onDraw(canvas); canvas.restore(); } public float getIndent(float distance){ float x_vertex = MAX_INDENT; DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); float y_vertex = displayMetrics.heightPixels / 2 / displayMetrics.density; double a = ( 0 - x_vertex ) / ( Math.pow(( 0 - y_vertex), 2) ) ; float indent = (float) (a * Math.pow((distance - y_vertex), 2) + x_vertex); return indent; } }
La segunda cosa que tenía que hacer era sobreescribir la clase ListView, hacerla implementar OnScrollListener y llamar a setOnScrollListener(this);
. Ahora puedo desplazarme por la lista, sigue la ecuación que pongo en la vista.
public class HalfCircleListView extends ListView implements AbsListView.OnScrollListener { public HalfCircleListView(Context context) { super(context); setOnScrollListener(this); } @Override public void onScrollStateChanged(AbsListView absListView, int i) { //Ignored } @Override public void onScroll(AbsListView absListView, int i, int i2, int i3) { absListView.invalidateViews(); } }
Puedes descargar la fuente completa de mi Gist .
Estado inicial Estado desplazado
Como se puede ver mi matemáticas es un poco fuera … Yo uso una parábola vs un círculo, por lo que tendrá que ser cambiado.
Puede aumentar / disminuir el margen izquierdo para cada vista devuelta en su adaptador getView (). Así, por ejemplo, para la primera mitad de sus puntos de vista, aumenta el margen de cada elemento, por ejemplo, 20 píxeles (int margin = index * 20), y disminuya en consecuencia para la segunda mitad de las vistas.
Esto necesita una gran cantidad de ajuste fino para realmente parece una lista circular, por supuesto, pero creo que tienes la idea.
Intentaría usar un widget de menú radial existente si es posible. Éste pretende apoyar un menú radial semicircular. No estoy seguro de si este hace o no.
Puede ser esto le ayudará,
Mira aquí
Mira esto
Espero que esto te ayudará 🙂
I encontrar un lograr lo que quieres y aquí es zhe github fuente https://github.com/sam85/MySample/tree/master/CircleLauncher
Después de algunos más goggling, encontré una solución.
Es relativamente optimizado y configurable como un ListView normal.
Aquí hay fragmentos de código principal:
CircularListView.java
package com.makotokw.android.widget; import android.annotation.TargetApi; import android.content.Context; import android.database.DataSetObserver; import android.os.Build; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.ListAdapter; import android.widget.ListView; public class CircularListView extends ListView implements AbsListView.OnScrollListener { private static final String TAG = CircularListView.class.getSimpleName(); private static final int REPEAT_COUNT = 3; private int mItemHeight = 0; private CircularListViewListener mCircularListViewListener; private InfiniteListAdapter mInfiniteListAdapter; private boolean mEnableInfiniteScrolling = true; private CircularListViewContentAlignment mCircularListViewContentAlignment = CircularListViewContentAlignment.Left; private double mRadius = -1; private int mSmoothScrollDuration = 80; public CircularListView(Context context) { this(context, null); } public CircularListView(Context context, AttributeSet attrs) { this(context, attrs, android.R.attr.listViewStyle); } public CircularListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setOnScrollListener(this); setClipChildren(false); setEnableInfiniteScrolling(true); } public void setAdapter(ListAdapter adapter) { mInfiniteListAdapter = new InfiniteListAdapter(adapter); mInfiniteListAdapter.setEnableInfiniteScrolling(mEnableInfiniteScrolling); super.setAdapter(mInfiniteListAdapter); } public CircularListViewListener getCircularListViewListener() { return mCircularListViewListener; } public void setCircularListViewListener(CircularListViewListener circularListViewListener) { this.mCircularListViewListener = circularListViewListener; } public void setEnableInfiniteScrolling(boolean enableInfiniteScrolling) { mEnableInfiniteScrolling = enableInfiniteScrolling; if (mInfiniteListAdapter != null) { mInfiniteListAdapter.setEnableInfiniteScrolling(enableInfiniteScrolling); } if (mEnableInfiniteScrolling) { setHorizontalScrollBarEnabled(false); setVerticalScrollBarEnabled(false); } } public CircularListViewContentAlignment getCircularListViewContentAlignment() { return mCircularListViewContentAlignment; } public void setCircularListViewContentAlignment( CircularListViewContentAlignment circularListViewContentAlignment) { if (mCircularListViewContentAlignment != circularListViewContentAlignment) { mCircularListViewContentAlignment = circularListViewContentAlignment; requestLayout(); } } public double getRadius() { return mRadius; } public void setRadius(double radius) { if (this.mRadius != radius) { this.mRadius = radius; requestLayout(); } } public int getCentralPosition() { double vCenterPos = getHeight() / 2.0f; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (child != null) { if (child.getTop() <= vCenterPos && child.getTop() + child.getHeight() >= vCenterPos) { return getFirstVisiblePosition() + i; } } } return -1; } public View getCentralChild() { int pos = getCentralPosition(); if (pos != -1) { return getChildAt(pos - getFirstVisiblePosition()); } return null; } public void scrollFirstItemToCenter() { if (!mEnableInfiniteScrolling) { return; } int realTotalItemCount = mInfiniteListAdapter.getRealCount(); if (realTotalItemCount > 0) { setSelectionFromTop(realTotalItemCount, getBaseCentralChildTop()); } } public int getBaseCentralChildTop() { int itemHeight = getItemHeight(); if (itemHeight > 0) { return getHeight() / 2 - itemHeight / 2; } return 0; } public int getItemHeight() { if (mItemHeight == 0) { View child = getChildAt(0); if (child != null) { mItemHeight = child.getHeight(); } } return mItemHeight; } public void setSelectionAndMoveToCenter(int position) { if (!mEnableInfiniteScrolling) { return; } int realTotalItemCount = mInfiniteListAdapter.getRealCount(); if (realTotalItemCount == 0) { return; } position = position % realTotalItemCount; int centralPosition = getCentralPosition() % realTotalItemCount; int y = getBaseCentralChildTop(); if (centralPosition == position) { View centralView = getCentralChild(); y = centralView.getTop(); } setSelectionFromTop(position + realTotalItemCount, y); } @TargetApi(Build.VERSION_CODES.FROYO) @Override public boolean dispatchKeyEvent(KeyEvent event) { if (mEnableInfiniteScrolling) { if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_UP: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { smoothScrollBy(mItemHeight, mSmoothScrollDuration); return true; } break; case KeyEvent.KEYCODE_DPAD_DOWN: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { smoothScrollBy(-mItemHeight, mSmoothScrollDuration); return true; } break; default: break; } } } return super.dispatchKeyEvent(event); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE) { if (!isInTouchMode()) { setSelectionAndMoveToCenter(getCentralPosition()); } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (!mEnableInfiniteScrolling) { return; } View itemView = this.getChildAt(0); if (itemView == null) { return; } int realTotalItemCount = mInfiniteListAdapter.getRealCount(); if (realTotalItemCount == 0) { return; } if (mItemHeight == 0) { mItemHeight = itemView.getHeight(); } if (firstVisibleItem == 0) { // scroll one unit this.setSelectionFromTop(realTotalItemCount, itemView.getTop()); } if (totalItemCount == firstVisibleItem + visibleItemCount) { // back one unit this.setSelectionFromTop(firstVisibleItem - realTotalItemCount, itemView.getTop()); } if (mCircularListViewContentAlignment != CircularListViewContentAlignment.None) { double viewHalfHeight = view.getHeight() / 2.0f; double vRadius = view.getHeight(); double hRadius = view.getWidth(); double yRadius = (view.getHeight() + mItemHeight) / 2.0f; double xRadius = (vRadius < hRadius) ? vRadius : hRadius; if (mRadius > 0) { xRadius = mRadius; } for (int i = 0; i < visibleItemCount; i++) { itemView = this.getChildAt(i); if (itemView != null) { double y = Math.abs(viewHalfHeight - (itemView.getTop() + (itemView.getHeight() / 2.0f))); y = Math.min(y, yRadius); double angle = Math.asin(y / yRadius); double x = xRadius * Math.cos(angle); if (mCircularListViewContentAlignment == CircularListViewContentAlignment.Left) { x -= xRadius; } else { x = xRadius / 2 - x; } itemView.scrollTo((int) x, 0); } } } else { for (int i = 0; i < visibleItemCount; i++) { itemView = this.getChildAt(i); if (itemView != null) { itemView.scrollTo(0, 0); } } } if (mCircularListViewListener != null) { mCircularListViewListener.onCircularLayoutFinished(this, firstVisibleItem, visibleItemCount, totalItemCount); } } class InfiniteListAdapter implements ListAdapter { private boolean mEnableInfiniteScrolling = true; private ListAdapter mCoreAdapter; public InfiniteListAdapter(ListAdapter coreAdapter) { mCoreAdapter = coreAdapter; } private void setEnableInfiniteScrolling(boolean enableInfiniteScrolling) { mEnableInfiniteScrolling = enableInfiniteScrolling; } public int getRealCount() { return mCoreAdapter.getCount(); } public int positionToIndex(int position) { int count = mCoreAdapter.getCount(); return (count == 0) ? 0 : position % count; } @Override public void registerDataSetObserver(DataSetObserver observer) { mCoreAdapter.registerDataSetObserver(observer); } @Override public void unregisterDataSetObserver(DataSetObserver observer) { mCoreAdapter.unregisterDataSetObserver(observer); } @Override public int getCount() { int count = mCoreAdapter.getCount(); return (mEnableInfiniteScrolling) ? count * REPEAT_COUNT : count; } @Override public Object getItem(int position) { return mCoreAdapter.getItem(this.positionToIndex(position)); } @Override public long getItemId(int position) { return mCoreAdapter.getItemId(this.positionToIndex(position)); } @Override public boolean hasStableIds() { return mCoreAdapter.hasStableIds(); } @Override public View getView(int position, View convertView, ViewGroup parent) { return mCoreAdapter.getView(this.positionToIndex(position), convertView, parent); } @Override public int getItemViewType(int position) { return mCoreAdapter.getItemViewType(this.positionToIndex(position)); } @Override public int getViewTypeCount() { return mCoreAdapter.getViewTypeCount(); } @Override public boolean isEmpty() { return mCoreAdapter.isEmpty(); } @Override public boolean areAllItemsEnabled() { return mCoreAdapter.areAllItemsEnabled(); } @Override public boolean isEnabled(int position) { return mCoreAdapter.isEnabled(this.positionToIndex(position)); } } } **CircularListViewContentAlignment.java** package com.makotokw.android.widget; public enum CircularListViewContentAlignment { None, Left, Right } **CircularListViewListener.java** package com.makotokw.android.widget; public interface CircularListViewListener { void onCircularLayoutFinished(CircularListView circularListView, int firstVisibleItem, int visibleItemCount, int totalItemCount); }
Para más aclaraciones, también puedes ver mi entrada en el blog y comentar sobre eso. Puede descargar la aplicación de ejemplo para eclipse.
Mi blog es:
http://androidpantiii.blogspot.in/2015/11/half-circular-list-view-there-were.html
- Error de visualización jónica si el gps está desactivado
- Diseño de material Android NestedScrollView / CollapsingToolbarLayout comportamiento extraño apertura del teclado