如何基于Android平板电脑的轮播开发UI屏幕

时间:2015-02-17 10:39:31

标签: android android-layout android-fragments tablet

我有一个屏幕,其中有两个容器一个接一个地垂直放置,First One包含放置在第二个容器中的轮播视图显示的组件信息。

如何实现这些,要使用的UI组件是什么,对于Carousel View任何演示代码都会很有用。对于第一个组件,我应该使用fragements还是UI Component。

如果我对应该使用的UI小部件有好的想法会更好

3 个答案:

答案 0 :(得分:0)

如果你想要一种旋转木马般的感觉,你可以将一个视图设为ViewAdapter,另一个设为片段。在场景后面,您可以使用回调或e.g. Otto同步数据。 使用ViewAdapter的不错功能,您可以轻扫图片和另一侧的片段用正确的文字刷新。

答案 1 :(得分:0)

您可以尝试创建carousel

准备工作

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Carousel">
        <attr name="android:gravity" /> 
        <attr name="android:animationDuration" />
        <attr name="UseReflection" format="boolean"/>
        <attr name="Items" format="integer"/>
        <attr name="SelectedItem" format="integer"/>
        <attr name="maxTheta" format="float"/>
        <attr name="minQuantity" format="integer"/>
        <attr name="maxQuantity" format="integer"/>
    </declare-styleable>    
</resources>

轮播项目类

public class CarouselImageView extends ImageView 
    implements Comparable<carouselimageview> {

    private int index;
    private float currentAngle;
    private float x;
    private float y;
    private float z;
    private boolean drawn;

    public CarouselImageView(Context context) {
        this(context, null, 0);
    }   

    public CarouselImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CarouselImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public int compareTo(CarouselImageView another) {
        return (int)(another.z – this.z);
    }

    …
}
</carouselimageview>

Rotator Class

public class Rotator {
    private int mMode;
    private float mStartAngle;
    private float mCurrAngle;

    private long mStartTime;
    private long mDuration;

    private float mDeltaAngle;

    private boolean mFinished;

    private float mCoeffVelocity = 0.05f;
    private float mVelocity;

    private static final int DEFAULT_DURATION = 250;
    private static final int SCROLL_MODE = 0;
    private static final int FLING_MODE = 1;

    private final float mDeceleration = 240.0f;


    /**
     * Create a Scroller with the specified interpolator. If the interpolator is
     * null, the default (viscous) interpolator will be used.
     */
    public Rotator(Context context) {
        mFinished = true;
    }

    /**
     * 
     * Returns whether the scroller has finished scrolling.
     * 
     * @return True if the scroller has finished scrolling, false otherwise.
     */
    public final boolean isFinished() {
        return mFinished;
    }

    /**
     * Force the finished field to a particular value.
     *  
     * @param finished The new finished value.
     */
    public final void forceFinished(boolean finished) {
        mFinished = finished;
    }

    /**
     * Returns how long the scroll event will take, in milliseconds.
     * 
     * @return The duration of the scroll in milliseconds.
     */
    public final long getDuration() {
        return mDuration;
    }

    /**
     * Returns the current X offset in the scroll. 
     * 
     * @return The new X offset as an absolute distance from the origin.
     */
    public final float getCurrAngle() {
        return mCurrAngle;
    }   

    /**
     * @hide
     * Returns the current velocity.
     *
     * @return The original velocity less the deceleration. Result may be
     * negative.
     */
    public float getCurrVelocity() {
        return mCoeffVelocity * mVelocity - mDeceleration * timePassed() /* / 2000.0f*/;
    }

    /**
     * Returns the start X offset in the scroll. 
     * 
     * @return The start X offset as an absolute distance from the origin.
     */
    public final float getStartAngle() {
        return mStartAngle;
    }           

    /**
     * Returns the time elapsed since the beginning of the scrolling.
     *
     * @return The elapsed time in milliseconds.
     */
    public int timePassed() {
        return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
    }

    /**
     * Extend the scroll animation. This allows a running animation to scroll
     * further and longer, when used with {@link #setFinalX(int)} 
     * or {@link #setFinalY(int)}.
     *
     * @param extend Additional time to scroll in milliseconds.
     * @see #setFinalX(int)
     * @see #setFinalY(int)
     */
    public void extendDuration(int extend) {
        int passed = timePassed();
        mDuration = passed + extend;
        mFinished = false;
    }

    /**
     * Stops the animation. Contrary to {@link #forceFinished(boolean)},
     * aborting the animating cause the scroller to move to the final x and y
     * position
     *
     * @see #forceFinished(boolean)
     */
    public void abortAnimation() {
        mFinished = true;
    }        

    /**
     * Call this when you want to know the new location.  If it returns true,
     * the animation is not yet finished.  loc will be altered to provide the
     * new location.
     */ 
    public boolean computeAngleOffset()
    {
        if (mFinished) {
            return false;
        }

        long systemClock = AnimationUtils.currentAnimationTimeMillis();
        long timePassed = systemClock - mStartTime;

        if (timePassed < mDuration) {
            switch (mMode) {
                case SCROLL_MODE:

                    float sc = (float)timePassed / mDuration;
                        mCurrAngle = mStartAngle + Math.round(mDeltaAngle * sc);    
                    break;

                 case FLING_MODE:

                    float timePassedSeconds = timePassed / 1000.0f;
                    float distance;

                    if(mVelocity < 0)
                    {
                        distance = mCoeffVelocity * mVelocity * timePassedSeconds - 
                        (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);
                    }
                    else{
                        distance = -mCoeffVelocity * mVelocity * timePassedSeconds - 
                        (mDeceleration * timePassedSeconds * timePassedSeconds / 2.0f);
                    }

                    mCurrAngle = mStartAngle - Math.signum(mVelocity)*Math.round(distance);

                    break;                    
            }
            return true;
        }
        else
        {
            mFinished = true;
            return false;
        }
    }    

    /**
     * Start scrolling by providing a starting point and the distance to travel.
     * 
     * @param startX Starting horizontal scroll offset in pixels. Positive
     *        numbers will scroll the content to the left.
     * @param startY Starting vertical scroll offset in pixels. Positive numbers
     *        will scroll the content up.
     * @param dx Horizontal distance to travel. Positive numbers will scroll the
     *        content to the left.
     * @param dy Vertical distance to travel. Positive numbers will scroll the
     *        content up.
     * @param duration Duration of the scroll in milliseconds.
     */
    public void startRotate(float startAngle, float dAngle, int duration) {
        mMode = SCROLL_MODE;
        mFinished = false;
        mDuration = duration;
        mStartTime = AnimationUtils.currentAnimationTimeMillis();
        mStartAngle = startAngle;
        mDeltaAngle = dAngle;
    }    

    /**
     * Start scrolling by providing a starting point and the distance to travel.
     * The scroll will use the default value of 250 milliseconds for the
     * duration.
     * 
     * @param startX Starting horizontal scroll offset in pixels. Positive
     *        numbers will scroll the content to the left.
     * @param startY Starting vertical scroll offset in pixels. Positive numbers
     *        will scroll the content up.
     * @param dx Horizontal distance to travel. Positive numbers will scroll the
     *        content to the left.
     * @param dy Vertical distance to travel. Positive numbers will scroll the
     *        content up.
     */
    public void startRotate(float startAngle, float dAngle) {
        startRotate(startAngle, dAngle, DEFAULT_DURATION);
    }

    /**
     * Start scrolling based on a fling gesture. The distance travelled will
     * depend on the initial velocity of the fling.
     * 
     * @param velocityAngle Initial velocity of the fling (X) 
     * measured in pixels per second.
     */
    public void fling(float velocityAngle) {

        mMode = FLING_MODE;
        mFinished = false;

        float velocity = velocityAngle;

        mVelocity = velocity;
        mDuration = (int)(1000.0f * Math.sqrt(2.0f * mCoeffVelocity * 
                Math.abs(velocity)/mDeceleration));

        mStartTime = AnimationUtils.currentAnimationTimeMillis();        
    }
}

轮播类

private class FlingRotateRunnable implements Runnable {

    /**
     * Tracks the decay of a fling rotation
     */     
    private Rotator mRotator;

    /**
     * Angle value reported by mRotator on the previous fling
     */
    private float mLastFlingAngle;

    /**
     * Constructor
     */
    public FlingRotateRunnable(){
        mRotator = new Rotator(getContext());
    }

    private void startCommon() {
        // Remove any pending flings
        removeCallbacks(this);
    }

    public void startUsingVelocity(float initialVelocity) {
        if (initialVelocity == 0) return;

        startCommon();

        mLastFlingAngle = 0.0f;

        mRotator.fling(initialVelocity);

        post(this);
    }               

    public void startUsingDistance(float deltaAngle) {
        if (deltaAngle == 0) return;

        startCommon();

        mLastFlingAngle = 0;
        synchronized(this)
        {
            mRotator.startRotate(0.0f, -deltaAngle, mAnimationDuration);
        }
        post(this);
    }

    public void stop(boolean scrollIntoSlots) {
        removeCallbacks(this);
        endFling(scrollIntoSlots);
    }        

    private void endFling(boolean scrollIntoSlots) {
        /*
         * Force the scroller's status to finished (without setting its
         * position to the end)
         */
        synchronized(this){
            mRotator.forceFinished(true);
        }

        if (scrollIntoSlots) scrollIntoSlots();
    }

    public void run() {
        if (Carousel.this.getChildCount() == 0) {
            endFling(true);
            return;
        }           

        mShouldStopFling = false;

        final Rotator rotator;
        final float angle;
        boolean more;
        synchronized(this){
            rotator = mRotator;
            more = rotator.computeAngleOffset();
            angle = rotator.getCurrAngle();             
        }            

        // Flip sign to convert finger direction to list items direction
        // (e.g. finger moving down means list is moving towards the top)
        float delta = mLastFlingAngle - angle;                        

        //////// Should be reworked
        trackMotionScroll(delta);

        if (more && !mShouldStopFling) {
            mLastFlingAngle = angle;
            post(this);
        } else {
            mLastFlingAngle = 0.0f;
            endFling(true);
        }              
}       

}

布局

/**
 * Setting up images
 */
void layout(int delta, boolean animate){

    if (mDataChanged) {
        handleDataChanged();
    }

    // Handle an empty gallery by removing all views.
    if (this.getCount() == 0) {
        resetList();
        return;
    }

    // Update to the new selected position.
    if (mNextSelectedPosition >= 0) {
        setSelectedPositionInt(mNextSelectedPosition);
    }        

    // All views go in recycler while we are in layout
    recycleAllViews();        

    // Clear out old views
    detachAllViewsFromParent();


    int count = getAdapter().getCount();
    float angleUnit = 360.0f / count;

    float angleOffset = mSelectedPosition * angleUnit;
    for(int i = 0; i< getAdapter().getCount(); i++){
        float angle = angleUnit * i - angleOffset;
        if(angle < 0.0f)
            angle = 360.0f + angle;
        makeAndAddView(i, angle);           
    }

    // Flush any cached views that did not get reused above
    mRecycler.clear();

    invalidate();

    setNextSelectedPositionInt(mSelectedPosition);

    checkSelectionChanged();

    ////////mDataChanged = false;
    mNeedSync = false;

    updateSelectedItemMetadata();
    }

制作并添加视图

private void makeAndAddView(int position, float angleOffset) {
    CarouselImageView child;

    if (!mDataChanged) {
        child = (CarouselImageView)mRecycler.get(position);
        if (child != null) {

            // Position the view
            setUpChild(child, child.getIndex(), angleOffset);
        }
        else
        {
            // Nothing found in the recycler -- ask the adapter for a view
            child = (CarouselImageView)mAdapter.getView(position, null, this);

            // Position the view
            setUpChild(child, child.getIndex(), angleOffset);               
        }
        return;
    }

    // Nothing found in the recycler -- ask the adapter for a view
    child = (CarouselImageView)mAdapter.getView(position, null, this);

    // Position the view
    setUpChild(child, child.getIndex(), angleOffset);
}      

private void setUpChild(CarouselImageView child, int index, float angleOffset) {

    // Ignore any layout parameters for child, use wrap content
    addViewInLayout(child, -1 /*index*/, generateDefaultLayoutParams());

    child.setSelected(index == this.mSelectedPosition);

    int h;
    int w;

    if(mInLayout)
    {
        h = (this.getMeasuredHeight() - 
    this.getPaddingBottom() - this.getPaddingTop())/3;
        w = this.getMeasuredWidth() - 
    this.getPaddingLeft() - this.getPaddingRight(); 
    }
    else
    {
        h = this.getHeight()/3;
        w = this.getWidth();            
    }

    child.setCurrentAngle(angleOffset);
    Calculate3DPosition(child, w, angleOffset);

    // Measure child
    child.measure(w, h);

    int childLeft;

    // Position vertically based on gravity setting
    int childTop = calculateTop(child, true);

    childLeft = 0;

    child.layout(childLeft, childTop, w, h);
} 

跟踪动画滚动

void trackMotionScroll(float deltaAngle) {

    if (getChildCount() == 0) {
        return;
    }

    for(int i = 0; i < getAdapter().getCount(); i++){
        CarouselImageView child = (CarouselImageView)getAdapter().getView(i, null, null);
        float angle = child.getCurrentAngle();
        angle += deltaAngle;
        while(angle > 360.0f)
            angle -= 360.0f;
        while(angle < 0.0f)
            angle += 360.0f;
        child.setCurrentAngle(angle);
        Calculate3DPosition(child, getWidth(), angle);          
    }

    // Clear unused views
    mRecycler.clear();        

    invalidate();
}

滚动到插槽

/**
 * Brings an item with nearest to 0 degrees angle to this angle and sets it selected 
 */
private void scrollIntoSlots(){

    // Nothing to do
    if (getChildCount() == 0 || mSelectedChild == null) return;

    // get nearest item to the 0 degrees angle
    // Sort itmes and get nearest angle
    float angle; 
    int position;

    ArrayList<carouselimageview> arr = new ArrayList<carouselimageview>();

    for(int i = 0; i < getAdapter().getCount(); i++)
        arr.add(((CarouselImageView)getAdapter().getView(i, null, null)));

    Collections.sort(arr, new Comparator<carouselimageview>(){
        @Override
        public int compare(CarouselImageView c1, CarouselImageView c2) {
            int a1 = (int)c1.getCurrentAngle();
            if(a1 > 180)
                a1 = 360 - a1;
            int a2 = (int)c2.getCurrentAngle();
            if(a2 > 180)
                a2 = 360 - a2;
            return (a1 - a2) ;
        }           
    });        

    angle = arr.get(0).getCurrentAngle();

    // Make it minimum to rotate
    if(angle > 180.0f)
        angle = -(360.0f - angle);

    // Start rotation if needed
    if(angle != 0.0f)
    {
        mFlingRunnable.startUsingDistance(-angle);
    }
    else
    {
        // Set selected position
        position = arr.get(0).getIndex();
        setSelectedPositionInt(position);
        onFinishedMovement();
    }        
}

滚动到子

void scrollToChild(int i){      

    CarouselImageView view = (CarouselImageView)getAdapter().getView(i, null, null);
    float angle = view.getCurrentAngle();

    if(angle == 0)
        return;

    if(angle > 180.0f)
        angle = 360.0f - angle;
    else
        angle = -angle;

        mFlingRunnable.startUsingDistance(-angle);
}

这是Calculate3DPosition方法:

private void Calculate3DPosition
(CarouselImageView child, int diameter, float angleOffset){
    angleOffset = angleOffset * (float)(Math.PI/180.0f);

    float x = -(float)(diameter/2*Math.sin(angleOffset));
    float z = diameter/2 * (1.0f - (float)Math.cos(angleOffset));
    float y = - getHeight()/2 + (float) (z * Math.sin(mTheta));

    child.setX(x);
    child.setZ(z);
    child.setY(y);
}

getChildStaticTransformation

protected boolean getChildStaticTransformation
    (View child, Transformation transformation) {

    transformation.clear();
    transformation.setTransformationType(Transformation.TYPE_MATRIX);

    // Center of the item
    float centerX = (float)child.getWidth()/2, centerY = (float)child.getHeight()/2;

    // Save camera
    mCamera.save();

    // Translate the item to it's coordinates
    final Matrix matrix = transformation.getMatrix();
    mCamera.translate(((CarouselImageView)child).getX(), 
                ((CarouselImageView)child).getY(), 
                ((CarouselImageView)child).getZ());

    // Align the item
    mCamera.getMatrix(matrix);
    matrix.preTranslate(-centerX, -centerY);
    matrix.postTranslate(centerX, centerY);

    // Restore camera
    mCamera.restore();      

    return true;
} 

getChildDrawingOrder

protected int getChildDrawingOrder(int childCount, int i) {

        // Sort Carousel items by z coordinate in reverse order
        ArrayList<carouselimageview> sl = new ArrayList<carouselimageview>();
        for(int j = 0; j < childCount; j++)
        {
            CarouselImageView view = (CarouselImageView)getAdapter().
                        getView(j,null, null);
            if(i == 0)
                view.setDrawn(false);
            sl.add((CarouselImageView)getAdapter().getView(j,null, null));
        }

        Collections.sort(sl);

        // Get first undrawn item in array and get result index
        int idx = 0;

        for(CarouselImageView civ : sl)
        {
            if(!civ.isDrawn())
            {
                civ.setDrawn(true);
                idx = civ.getIndex();
                break;
            }
        }

        return idx;
    }

答案 2 :(得分:-2)

编辑以避免荒谬

您应该尝试使用HorizontalScrollView