Циркуляр ListView (элементы на полукруге)

Я пытаюсь сделать Circular ListView с элементами списка, расположенными на Half Circle. Он должен выглядеть примерно так:

Введите описание изображения здесь

Был связанный пост, но он был закрыт.

Я создал свой собственный Circular Custom ListView, и он отлично работает, но моя проблема заключается в том, что я не могу упорядочить метод «Половина круга», как он показан на изображении. Я попробовал несколько вещей, но это было бесполезно, я не знаю, как это сделать.

Solutions Collecting From Web of "Циркуляр ListView (элементы на полукруге)"

Поэтому, когда я сделал пример приложения для демонстрации, мне пришлось сделать 2 вещи.

Во-первых, было редактирование onDraw(Canvas) на моем пользовательском представлении. Это может быть любое представление, я делаю это TextView для простоты. Это позволяет мне подтолкнуть взгляд на основе уравнения.

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

Второе, что мне нужно было сделать, это переопределить класс ListView, сделать его реализацией OnScrollListener и вызвать setOnScrollListener(this); , Теперь я могу прокручивать список, это следует за уравнением, которое я ввел в представление.

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

Вы можете скачать полный источник из моего Gist .

Начальное состояние Начальное состояние просмотра Прокрученное состояние Прокрутка вниз

Поскольку вы можете видеть, что моя математика немного ушла … Я использую параболу против круга, так что ее нужно будет изменить.

Вы можете увеличить / уменьшить левое поле для каждого вида, возвращаемого в getView () вашего адаптера. Так, например, для первой половины ваших просмотров вы увеличиваете запас для каждого элемента, например, 20 пикселей (int margin = index * 20) и уменьшаете его соответственно для второй половины просмотров.

Конечно, это требует много тонкой настройки, чтобы действительно выглядеть круговым списком, но я думаю, что вы поняли эту идею.

Я попытался бы использовать существующий радиальный виджет меню, если это возможно. Он утверждает, что поддерживает полукруглое радиальное меню. Я не уверен, что это делает или нет.

Может быть, это поможет вам,

Глянь сюда

Видеть это

Надеюсь, что это поможет вам 🙂

Я нахожу достижение того, чего вы хотите, и вот источник zih github https://github.com/sam85/MySample/tree/master/CircleLauncher

Еще несколько очков, я нашел решение.
Он относительно оптимизирован и настраивается как обычный ListView.

Вот основные фрагменты кода:

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

Для получения более подробных сведений вы также можете увидеть мое сообщение в блоге и можете прокомментировать это. Вы можете загрузить пример приложения для eclipse.
Мой блог:
http://androidpantiii.blogspot.in/2015/11/half-circular-list-view-there-were.html