2013-04-27 98 views
8

我正在尝试使圆形列表视图与列表项目排列在半圆上。它应该是这个样子:圆形列表视图(在半圆上的项目)

enter image description here

有一个related post但它被关闭。

我做了我自己的圆形自定义ListView,它工作正常,但我的问题是,我不能安排列表项目半圆方式,因为它显示在图像上。我尝试了几件事,但它没用,我不知道该怎么做。

+0

是什么让你觉得这是一个'ListView'? – CommonsWare 2013-05-02 00:33:24

+0

这可能不是一个ListView,我只是有一个项目列表相当多,需要安排他们这样,但我不知道如何。 。 。没有必要成为一个ListView。 – Jilberta 2013-05-03 10:43:45

+0

应该像ListView一样滚动吗? – 2013-05-04 11:38:12

回答

18

所以当我制作示例应用程序来演示这个时,我不得不做两件事。

首先,在我的自定义视图上编辑了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下载完整的源代码。

初始状态 Initial State of the View 滚动状态 Scrolled to the bottom

正如你可以看到我的数学是有点过......我用一个抛物线VS了一圈,这样就必须改变。

+0

我越来越error.at getY() – Harshid 2013-07-08 10:08:24

+0

你得到什么错误? – Ethan 2013-07-08 13:37:38

+0

它说的是添加getY()这个方法。 – Harshid 2013-07-08 13:38:46

1

您可以为适配器的getView()中返回的每个视图增加/减少左边距。因此,例如,对于前半部分视图,您可以将每个项目的边距增加20像素(int margin = index * 20),并在后半部分视图中相应减小。

当然,这需要很多微调来真正看起来像一个循环列表,但我认为你明白了。

+0

是的我已经试图这样做,但有一个问题。当适配器首先使用getView时,它会为一个项目创建一个视图,但之后它将使用之前使用过的视图的视图,并且您无法说出它将使用哪个视图,因此看起来很乱。 – Jilberta 2013-05-03 10:50:26

+0

@Jilberta,问题有多大?每次执行该方法时,根据传递给getView()的位置设置边距。向我们显示您的代码,我们可能能够解决您的问题。 – TomTasche 2013-05-03 11:29:13

+0

@Jilberta,你是对的,你从回收视图扩展的listview,如果以某些方式重用,他们可能会看起来“搞砸”,但请记住,你也可以设置一个http://developer.android.com/reference /android/widget/AbsListView.html#setRecyclerListener(android.widget.AbsListView.RecyclerListener)清除事件,如果他们造成混乱。最好。 – Tom 2013-05-04 17:22:13

1

如果可能,我会尝试使用现有的径向菜单小部件。 This one声称支持半圆形放射状菜单。我不确定this one是否有效。

+0

没有屏幕截图在文档中。我怎样才能想象这个观点? – 2016-07-18 14:42:52

0

经过一些更多护目镜,我找到了一个解决方案。
它是相对优化和可配置像一个正常的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