扩展 Gallery 并覆盖 drawChild。
对于每个需要绘制的子元素,都会调用drawChild。
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
final int left = child.getLeft();
int adjustedXOrigin = left - (getWidth() / 2) + (child.getWidth()/2);
int newtop = (int) (ellipseYOffset - Math.sqrt( ellipseMinor2 * (1 - ((Math.pow(adjustedXOrigin, 2)) / ellipseMajor2))));
newtop -= (child.getHeight() / 2);
if( newtop >= 0 )
{
child.layout(left, newtop, left + child.getWidth(), newtop + child.getHeight());
return super.drawChild(canvas, child, drawingTime);
}
return true;
}
在 onLayout 中我计算 ellipseYOffset。无论椭圆大小如何,这都会使中间选定的视图在视图中垂直居中。
ellipseYOffset = getMeasuredHeight() + (ellipseMinor - (getMeasuredHeight() / 2));
“if (newtop >= 0 )”部分是因为视图被随机绘制在奇怪的地方。这停止了。
编辑:完整代码
有一些动画的东西你不需要,我只是复制并粘贴了我的课程。
public class Carousel extends Gallery {
private static final float INITIAL_MINOR_RATIO = 0.75f;
private static final float INITIAL_MAJOR_RATIO = 1.0f;
private int mEllipseMajor;
private int mEllipseMinor;
private int mEllipseMajor2;
private int mEllipseMinor2;
private int mEllipseYOffset;
private Animation mGalleryAlphaOut;
private Animation mGalleryAlphaIn;
private OnAnimationEndListener mFadeInEndListener;
private OnAnimationEndListener mFadeOutEndListener;
private boolean mCustomEllipseDim = false;
private boolean mInfinite = true;
private int mXOff = 0;
private AnimationListener mFadeInAnimationListener = new AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationRepeat(Animation animation) {}
public void onAnimationEnd(Animation animation) {
if( mFadeInEndListener != null )
{
mFadeInEndListener.onAnimationEnd();
}
}
};
private AnimationListener mFadeOutAnimationListener = new AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationRepeat(Animation animation) {}
public void onAnimationEnd(Animation animation) {
if( mFadeOutEndListener != null )
{
mFadeOutEndListener.onAnimationEnd();
}
}
};
public Carousel(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public Carousel(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public Carousel(Context context) {
super(context);
init();
}
private void init()
{
setHorizontalFadingEdgeEnabled(false);
setCallbackDuringFling(true);
setUnselectedAlpha(1.0f);
setHapticFeedbackEnabled(false);
int dur = getResources().getInteger(R.integer.transition_dur);
mGalleryAlphaOut = AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_out);
mGalleryAlphaOut.setFillAfter(true);
mGalleryAlphaOut.setDuration(dur);
mGalleryAlphaOut.setAnimationListener(mFadeOutAnimationListener);
mGalleryAlphaIn = AnimationUtils.loadAnimation(getContext(), android.R.anim.fade_in);
mGalleryAlphaIn.setFillAfter(true);
mGalleryAlphaIn.setDuration(dur);
mGalleryAlphaIn.setAnimationListener(mFadeInAnimationListener);
}
public int getEllipseMajor() {
return mEllipseMajor;
}
public void setEllipseMajor(int ellipseMajor) {
if( ellipseMajor == 0 )
{
mCustomEllipseDim = false;
}
this.mEllipseMajor = ellipseMajor;
}
public int getEllipseMinor() {
return mEllipseMinor;
}
public void setEllipseMinor(int ellipseMinor) {
if( ellipseMinor == 0 )
{
mCustomEllipseDim = false;
}
this.mEllipseMinor = ellipseMinor;
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
final int left = child.getLeft();
final int childWidth = child.getWidth();
final int childHeight = child.getHeight();
int adjustedXOrigin = left - mXOff + (childWidth>>1);
int newtop = (int) (mEllipseYOffset - Math.sqrt( mEllipseMinor2 * (1 - ((Math.pow(adjustedXOrigin, 2)) / mEllipseMajor2))));
newtop -= (childHeight>>1);
if( newtop >= 0 )
{
child.layout(left, newtop, left + childWidth, newtop + childHeight);
return super.drawChild(canvas, child, drawingTime);
}
return true;
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if( !mCustomEllipseDim )
{
mEllipseMajor = (int) (getMeasuredWidth() * INITIAL_MAJOR_RATIO + 0.5f);
mEllipseMinor = (int) (getMeasuredHeight() * INITIAL_MINOR_RATIO + 0.5f);
mEllipseMajor2 = (int) Math.pow( mEllipseMajor, 2 );
mEllipseMinor2 = (int) Math.pow( mEllipseMinor, 2 );
}
mEllipseYOffset = getMeasuredHeight() + (mEllipseMinor - (getMeasuredHeight() / 2));
mXOff = (getWidth() / 2);
}
@Override
public void setAdapter(SpinnerAdapter adapter) {
super.setAdapter(adapter);
if( mInfinite )
{
resetPosition();
}
}
public void resetPosition()
{
int pos = Integer.MAX_VALUE / 2;
if( getAdapter() != null && getAdapter().getClass() == CarouselAdapter.class )
{
int size = ((CarouselAdapter)getAdapter()).getList().size();
if( size > 2 )
pos = pos - (pos % ((CarouselAdapter)getAdapter()).getList().size());
else
pos = 0;
setSelection(pos);
}
}
public OnAnimationEndListener getFadeInEndListener() {
return mFadeInEndListener;
}
public void setFadeInEndListener(OnAnimationEndListener fadeInEndListener) {
this.mFadeInEndListener = fadeInEndListener;
}
public OnAnimationEndListener getFadeOutEndListener() {
return mFadeOutEndListener;
}
public void setFadeOutEndListener(OnAnimationEndListener fadeOutEndListener) {
this.mFadeOutEndListener = fadeOutEndListener;
}
public void fadeIn()
{
startAnimation(mGalleryAlphaIn);
}
public void fadeOut()
{
startAnimation(mGalleryAlphaOut);
}
public interface OnAnimationEndListener
{
public abstract void onAnimationEnd();
}
//This disables the effect of a vehicle becoming focused when it is clicked.
@Override
public boolean onSingleTapUp(MotionEvent e) {
if( getAdapter() != null )
{
if( pointToPosition((int)e.getX(), (int)e.getY()) != getSelectedItemPosition() )
return true;
else
return super.onSingleTapUp(e);
}
else
return true;
}
}