android實現uc和墨跡天氣那樣的左右拖動效果

fmms 12年前發布 | 20K 次閱讀 Android開發 移動開發 Android

import android.app.Activity;
import android.os.Bundle;

import android.content.Context; import android.graphics.Color; import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TableLayout; import android.widget.TextView;

public class FlingGalleryActivity extends Activity { private final int color_red = Color.argb(100, 200, 0, 0); private final int color_green = Color.argb(100, 0, 200, 0); private final int color_blue = Color.argb(100, 0, 0, 200); private final int color_yellow = Color.argb(100, 200, 200, 0); private final int color_purple = Color.argb(100, 200, 0, 200);

private final String[] mLabelArray = {"View1", "View2", "View3", "View4", "View5"};
private final int[] mColorArray = {color_red, color_green, color_blue, color_yellow, color_purple};

private FlingGallery mGallery;
private CheckBox mCheckBox;

// Note: The following handler is critical to correct function of
// the FlingGallery class. This enables the FlingGallery class to
// detect when the motion event has ended by finger being lifted

@Override
public boolean onTouchEvent(MotionEvent event)
{
    return mGallery.onGalleryTouchEvent(event);
}

public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    mGallery = new FlingGallery(this);
    mGallery.setPaddingWidth(5);
    mGallery.setAdapter(new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, mLabelArray)
    {
        @Override
        public View getView(int position, View convertView, ViewGroup parent)
        {
            Log.d("111", "count="+position);

// if (convertView != null && convertView instanceof GalleryViewItem) // { // GalleryViewItem galleryView = (GalleryViewItem) convertView; // // galleryView.mEdit1.setText(""); // galleryView.mText1.setText(mLabelArray[position]); // galleryView.mText1.setBackgroundColor(mColorArray[position]); // galleryView.mText2.setText(mLabelArray[position]); // galleryView.mText2.setBackgroundColor(mColorArray[position]); //
// Log.d("111", "count="+position); //
// return galleryView; //
// }

            return new GalleryViewItem(getApplicationContext(), position);
        }
    });

    LinearLayout layout = new LinearLayout(getApplicationContext());
    layout.setOrientation(LinearLayout.VERTICAL);

    LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.MATCH_PARENT);

    layoutParams.setMargins(10, 10, 10, 10);
    layoutParams.weight = 1.0f;

    layout.addView(mGallery, layoutParams);

    mCheckBox = new CheckBox(getApplicationContext());
    mCheckBox.setText("Gallery is Circular");
    mCheckBox.setText("Gallery is Circular");
    mCheckBox.setPadding(50, 10, 0, 10);
    mCheckBox.setTextSize(30);
    mCheckBox.setChecked(true);
    mCheckBox.setOnClickListener(new OnClickListener()
    {
        @Override
        public void onClick(View view)
        {
            mGallery.setIsGalleryCircular(mCheckBox.isChecked());
        }
    });

    layout.addView(mCheckBox, new LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.MATCH_PARENT,
            LinearLayout.LayoutParams.WRAP_CONTENT));

    setContentView(layout);
}   

private class GalleryViewItem extends TableLayout
{
    private EditText mEdit1;
    private TextView mText1;
    private TextView mText2;
    private Button mButton1;
    private Button mButton2;

    public GalleryViewItem(Context context, int position)
    {
        super(context);

        this.setOrientation(LinearLayout.VERTICAL);

        this.setLayoutParams(new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT));

        mEdit1 = new EditText(context);

        this.addView(mEdit1, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT)); 

        mText1 = new TextView(context);
        mText1.setText(mLabelArray[position]);
        mText1.setTextSize(30);
        mText1.setGravity(Gravity.LEFT);
        mText1.setBackgroundColor(mColorArray[position]);

        this.addView(mText1, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT)); 

        mButton1 = new Button(context);
        mButton1.setText("<<");
        mButton1.setGravity(Gravity.LEFT);
        mButton1.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View view)
            {
                mGallery.movePrevious();
            }
        });

        this.addView(mButton1, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT)); 

        mButton2 = new Button(context);
        mButton2.setText(">>");
        mButton2.setGravity(Gravity.RIGHT);
        mButton2.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View view)
            {
                mGallery.moveNext();
            }
        });

        this.addView(mButton2, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.WRAP_CONTENT)); 

        mText2 = new TextView(context);
        mText2.setText(mLabelArray[position]);
        mText2.setTextSize(30);
        mText2.setGravity(Gravity.RIGHT);
        mText2.setBackgroundColor(mColorArray[position]);

        this.addView(mText2, new LinearLayout.LayoutParams(
                LinearLayout.LayoutParams.MATCH_PARENT,
                LinearLayout.LayoutParams.MATCH_PARENT, 1)); 
    }
}

}</pre>

FlingGallery
import android.content.Context;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.Transformation;
import android.widget.Adapter;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

// TODO:

// 1. In order to improve performance Cache screen bitmap and use for animation // 2. Establish superfluous memory allocations and delay or replace with reused objects // Probably need to make sure we are not allocating objects (strings, etc.) in loops

public class FlingGallery extends FrameLayout { // Constants

private final int swipe_min_distance = 120;
private final int swipe_max_off_path = 250;
private final int swipe_threshold_veloicty = 400;

// Properties

private int mViewPaddingWidth = 0;
private int mAnimationDuration = 250;
private float mSnapBorderRatio = 0.5f;
private boolean mIsGalleryCircular = true;

// Members

private int mGalleryWidth = 0;
private boolean mIsTouched = false;
private boolean mIsDragging = false;
private float mCurrentOffset = 0.0f;
private long mScrollTimestamp = 0;
private int mFlingDirection = 0;
private int mCurrentPosition = 0;
private int mCurrentViewNumber = 0;

private Context mContext;
private Adapter mAdapter;
private FlingGalleryView[] mViews;
private FlingGalleryAnimation mAnimation;
private GestureDetector mGestureDetector;
private Interpolator mDecelerateInterpolater;

public FlingGallery(Context context)
{
    super(context);

    mContext = context;
    mAdapter = null;

    mViews = new FlingGalleryView[3];
    mViews[0] = new FlingGalleryView(0, this);
    mViews[1] = new FlingGalleryView(1, this);
    mViews[2] = new FlingGalleryView(2, this);

    mAnimation = new FlingGalleryAnimation();
    mGestureDetector = new GestureDetector(new FlingGestureDetector());
    mDecelerateInterpolater = AnimationUtils.loadInterpolator(mContext, android.R.anim.decelerate_interpolator);
}

public void setPaddingWidth(int viewPaddingWidth)
{
    mViewPaddingWidth = viewPaddingWidth;
}

public void setAnimationDuration(int animationDuration)
{
    mAnimationDuration = animationDuration;
}

public void setSnapBorderRatio(float snapBorderRatio)
{
    mSnapBorderRatio = snapBorderRatio;
}

public void setIsGalleryCircular(boolean isGalleryCircular) 
{
    if (mIsGalleryCircular != isGalleryCircular)
    {
        mIsGalleryCircular = isGalleryCircular;

        if (mCurrentPosition == getFirstPosition())
        {
            // We need to reload the view immediately to the left to change it to circular view or blank
            mViews[getPrevViewNumber(mCurrentViewNumber)].recycleView(getPrevPosition(mCurrentPosition));           
        }

        if (mCurrentPosition == getLastPosition())
        {
            // We need to reload the view immediately to the right to change it to circular view or blank
            mViews[getNextViewNumber(mCurrentViewNumber)].recycleView(getNextPosition(mCurrentPosition));           
        }
    }
}

public int getGalleryCount()
{
    return (mAdapter == null) ? 0 : mAdapter.getCount();
}

public int getFirstPosition()
{
    return 0;
}

public int getLastPosition()
{
    return (getGalleryCount() == 0) ? 0 : getGalleryCount() - 1;
}

private int getPrevPosition(int relativePosition)
{
    int prevPosition = relativePosition - 1;

    if (prevPosition < getFirstPosition())
    {
        prevPosition = getFirstPosition() - 1;

        if (mIsGalleryCircular == true)
        {
            prevPosition = getLastPosition();
        }
    }

    return prevPosition;
}

private int getNextPosition(int relativePosition)
{
    int nextPosition = relativePosition + 1;

    if (nextPosition > getLastPosition())
    {
        nextPosition = getLastPosition() + 1;

        if (mIsGalleryCircular == true)
        {
            nextPosition = getFirstPosition();
        }
    }

    return nextPosition;
}

private int getPrevViewNumber(int relativeViewNumber)
{
    return (relativeViewNumber == 0) ? 2 : relativeViewNumber - 1;
}

private int getNextViewNumber(int relativeViewNumber)
{
    return (relativeViewNumber == 2) ? 0 : relativeViewNumber + 1;
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
{
    super.onLayout(changed, left, top, right, bottom);

    // Calculate our view width
    mGalleryWidth = right - left;

    if (changed == true)
    {
        // Position views at correct starting offsets
        mViews[0].setOffset(0, 0, mCurrentViewNumber);
        mViews[1].setOffset(0, 0, mCurrentViewNumber);
        mViews[2].setOffset(0, 0, mCurrentViewNumber);
    }
}

public void setAdapter(Adapter adapter)
{
    mAdapter = adapter;
    mCurrentPosition = 0;
    mCurrentViewNumber = 0;

    // Load the initial views from adapter
    mViews[0].recycleView(mCurrentPosition);
    mViews[1].recycleView(getNextPosition(mCurrentPosition));
    mViews[2].recycleView(getPrevPosition(mCurrentPosition));

    // Position views at correct starting offsets
    mViews[0].setOffset(0, 0, mCurrentViewNumber);
    mViews[1].setOffset(0, 0, mCurrentViewNumber);
    mViews[2].setOffset(0, 0, mCurrentViewNumber);
}

private int getViewOffset(int viewNumber, int relativeViewNumber)
{
    // Determine width including configured padding width
    int offsetWidth = mGalleryWidth + mViewPaddingWidth;

    // Position the previous view one measured width to left
    if (viewNumber == getPrevViewNumber(relativeViewNumber))
    {
        return offsetWidth;
    }

    // Position the next view one measured width to the right
    if (viewNumber == getNextViewNumber(relativeViewNumber))
    {
        return offsetWidth * -1;
    }

    return 0;
}

void movePrevious()
{
    // Slide to previous view
    mFlingDirection = 1;
    processGesture();
}

void moveNext()
{
    // Slide to next view
    mFlingDirection = -1;
    processGesture();
}

 @Override
 public boolean onKeyDown(int keyCode, KeyEvent event)
 {
    switch (keyCode)
    {
    case KeyEvent.KEYCODE_DPAD_LEFT:
        movePrevious();
        return true;

    case KeyEvent.KEYCODE_DPAD_RIGHT:
        moveNext();
        return true;

    case KeyEvent.KEYCODE_DPAD_CENTER:
    case KeyEvent.KEYCODE_ENTER:
    }

    return super.onKeyDown(keyCode, event);
}

public boolean onGalleryTouchEvent(MotionEvent event)
{
    boolean consumed = mGestureDetector.onTouchEvent(event);

    if (event.getAction() == MotionEvent.ACTION_UP)
    {
        if (mIsTouched || mIsDragging)
        {
            processScrollSnap();
            processGesture();
        }
    }

    return consumed;
}

void processGesture()
{
    int newViewNumber = mCurrentViewNumber;
    int reloadViewNumber = 0;
    int reloadPosition = 0;

    mIsTouched = false;
    mIsDragging = false;

    if (mFlingDirection > 0)
    {
        if (mCurrentPosition > getFirstPosition() || mIsGalleryCircular == true)
        {
            // Determine previous view and outgoing view to recycle
            newViewNumber = getPrevViewNumber(mCurrentViewNumber);
            mCurrentPosition = getPrevPosition(mCurrentPosition);
            reloadViewNumber = getNextViewNumber(mCurrentViewNumber); 
            reloadPosition = getPrevPosition(mCurrentPosition);
        }
    }

    if (mFlingDirection < 0)
    {
        if (mCurrentPosition < getLastPosition() || mIsGalleryCircular == true)
        {
            // Determine the next view and outgoing view to recycle
            newViewNumber = getNextViewNumber(mCurrentViewNumber);
            mCurrentPosition = getNextPosition(mCurrentPosition);
            reloadViewNumber = getPrevViewNumber(mCurrentViewNumber);
            reloadPosition = getNextPosition(mCurrentPosition);
        }
    }

    if (newViewNumber != mCurrentViewNumber)
    {
        mCurrentViewNumber = newViewNumber; 

        // Reload outgoing view from adapter in new position
        mViews[reloadViewNumber].recycleView(reloadPosition);
    }

    // Ensure input focus on the current view
    mViews[mCurrentViewNumber].requestFocus();

    // Run the slide animations for view transitions
    mAnimation.prepareAnimation(mCurrentViewNumber);
    this.startAnimation(mAnimation);

    // Reset fling state
    mFlingDirection = 0;
}

void processScrollSnap()
{
    // Snap to next view if scrolled passed snap position
    float rollEdgeWidth = mGalleryWidth * mSnapBorderRatio;
    int rollOffset = mGalleryWidth - (int) rollEdgeWidth;
    int currentOffset = mViews[mCurrentViewNumber].getCurrentOffset();

    if (currentOffset <= rollOffset * -1)
    {
        // Snap to previous view
        mFlingDirection = 1;
    }

    if (currentOffset >= rollOffset)
    {
        // Snap to next view
        mFlingDirection = -1;
    }
}

private class FlingGalleryView
{
    private int mViewNumber;
    private FrameLayout mParentLayout;

    private FrameLayout mInvalidLayout = null;
    private LinearLayout mInternalLayout = null;
    private View mExternalView = null;

    public FlingGalleryView(int viewNumber, FrameLayout parentLayout)
    {
        mViewNumber = viewNumber;
        mParentLayout = parentLayout;

        // Invalid layout is used when outside gallery
        mInvalidLayout = new FrameLayout(mContext);
        mInvalidLayout.setLayoutParams(new LinearLayout.LayoutParams( 
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        // Internal layout is permanent for duration
        mInternalLayout = new LinearLayout(mContext);
        mInternalLayout.setLayoutParams(new LinearLayout.LayoutParams( 
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        mParentLayout.addView(mInternalLayout);
    }

    public void recycleView(int newPosition)
    {
        if (mExternalView != null)
        {
            mInternalLayout.removeView(mExternalView);
        }

        if (mAdapter != null)
        {
            if (newPosition >= getFirstPosition() && newPosition <= getLastPosition())
            {
                mExternalView = mAdapter.getView(newPosition, mExternalView, mInternalLayout);
            }
            else
            {
                mExternalView = mInvalidLayout;
            }
        }

        if (mExternalView != null)
        {
            mInternalLayout.addView(mExternalView, new LinearLayout.LayoutParams( 
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        }
    }

    public void setOffset(int xOffset, int yOffset, int relativeViewNumber)
    {
        // Scroll the target view relative to its own position relative to currently displayed view
        mInternalLayout.scrollTo(getViewOffset(mViewNumber, relativeViewNumber) + xOffset, yOffset);
    }

    public int getCurrentOffset()
    {
        // Return the current scroll position
        return mInternalLayout.getScrollX();
    }

    public void requestFocus()
    {
        mInternalLayout.requestFocus();
    }
}

private class FlingGalleryAnimation extends Animation
{
    private boolean mIsAnimationInProgres;
    private int mRelativeViewNumber;
    private int mInitialOffset;
    private int mTargetOffset;
    private int mTargetDistance;    

    public FlingGalleryAnimation()
    {
        mIsAnimationInProgres = false;
        mRelativeViewNumber = 0;
        mInitialOffset = 0;
        mTargetOffset = 0;
        mTargetDistance = 0;
    }

    public void prepareAnimation(int relativeViewNumber)
    {
        // If we are animating relative to a new view
        if (mRelativeViewNumber != relativeViewNumber)
        {
            if (mIsAnimationInProgres == true)
            {
                // We only have three views so if requested again to animate in same direction we must snap 
                int newDirection = (relativeViewNumber == getPrevViewNumber(mRelativeViewNumber)) ? 1 : -1;
                int animDirection = (mTargetDistance < 0) ? 1 : -1; 

                // If animation in same direction
                if (animDirection == newDirection)
                {
                    // Ran out of time to animate so snap to the target offset
                    mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber);
                    mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber);
                    mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber); 
                }
            }

            // Set relative view number for animation
            mRelativeViewNumber = relativeViewNumber;
        }

        // Note: In this implementation the targetOffset will always be zero
        // as we are centering the view; but we include the calculations of
        // targetOffset and targetDistance for use in future implementations

        mInitialOffset = mViews[mRelativeViewNumber].getCurrentOffset();
        mTargetOffset = getViewOffset(mRelativeViewNumber, mRelativeViewNumber);
        mTargetDistance = mTargetOffset - mInitialOffset;

        // Configure base animation properties
        this.setDuration(mAnimationDuration);
        this.setInterpolator(mDecelerateInterpolater);

        // Start/continued animation
        mIsAnimationInProgres = true;
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation transformation)
    {
        // Ensure interpolatedTime does not over-shoot then calculate new offset
        interpolatedTime = (interpolatedTime > 1.0f) ? 1.0f : interpolatedTime;
        int offset = mInitialOffset + (int) (mTargetDistance * interpolatedTime);

        for (int viewNumber = 0; viewNumber < 3; viewNumber++)
        {
            // Only need to animate the visible views as the other view will always be off-screen
            if ((mTargetDistance > 0 && viewNumber != getNextViewNumber(mRelativeViewNumber)) ||
                (mTargetDistance < 0 && viewNumber != getPrevViewNumber(mRelativeViewNumber)))
            {
                mViews[viewNumber].setOffset(offset, 0, mRelativeViewNumber);
            }
        }
    }

    @Override
    public boolean getTransformation(long currentTime, Transformation outTransformation)
    {
        if (super.getTransformation(currentTime, outTransformation) == false)
        {
            // Perform final adjustment to offsets to cleanup animation
            mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber);
            mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber);
            mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber);

            // Reached the animation target
            mIsAnimationInProgres = false;

            return false;
        }

        // Cancel if the screen touched
        if (mIsTouched || mIsDragging)
        {
            // Note that at this point we still consider ourselves to be animating
            // because we have not yet reached the target offset; its just that the
            // user has temporarily interrupted the animation with a touch gesture

            return false;
        }

        return true;
    }
}

private class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener
{
    @Override
    public boolean onDown(MotionEvent e)
    {
        // Stop animation
        mIsTouched = true;

        // Reset fling state
        mFlingDirection = 0;
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
    {
        if (e2.getAction() == MotionEvent.ACTION_MOVE)
        {
            if (mIsDragging == false)
            {
                // Stop animation
                mIsTouched = true;

                // Reconfigure scroll
                mIsDragging = true;
                mFlingDirection = 0;
                mScrollTimestamp = System.currentTimeMillis();
                mCurrentOffset = mViews[mCurrentViewNumber].getCurrentOffset();
            }

            float maxVelocity = mGalleryWidth / (mAnimationDuration / 1000.0f);
            long timestampDelta = System.currentTimeMillis() - mScrollTimestamp;
            float maxScrollDelta = maxVelocity * (timestampDelta / 1000.0f); 
            float currentScrollDelta = e1.getX() - e2.getX();

            if (currentScrollDelta < maxScrollDelta * -1) currentScrollDelta = maxScrollDelta * -1;
            if (currentScrollDelta > maxScrollDelta) currentScrollDelta = maxScrollDelta;
            int scrollOffset = Math.round(mCurrentOffset + currentScrollDelta);

            // We can't scroll more than the width of our own frame layout
            if (scrollOffset >= mGalleryWidth) scrollOffset = mGalleryWidth;
            if (scrollOffset <= mGalleryWidth * -1) scrollOffset = mGalleryWidth * -1;

            mViews[0].setOffset(scrollOffset, 0, mCurrentViewNumber);
            mViews[1].setOffset(scrollOffset, 0, mCurrentViewNumber);
            mViews[2].setOffset(scrollOffset, 0, mCurrentViewNumber);
        }

        return false;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
    {
        if (Math.abs(e1.getY() - e2.getY()) <= swipe_max_off_path)
        {
            if (e2.getX() - e1.getX() > swipe_min_distance && Math.abs(velocityX) > swipe_threshold_veloicty)
            {
                movePrevious();
            }

            if(e1.getX() - e2.getX() > swipe_min_distance && Math.abs(velocityX) > swipe_threshold_veloicty)
            {
                moveNext();
            }
        }

        return false;
    }

    @Override
    public void onLongPress(MotionEvent e)
    {
        // Finalise scrolling
        mFlingDirection = 0;
        processGesture();
    }

    @Override
    public void onShowPress(MotionEvent e)
    {
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e)
    {
        // Reset fling state
        mFlingDirection = 0;
        return false;
    }
}

}</pre></h3>

 本文由用戶 fmms 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!