怎样在android实现uc和墨迹天气那样的左右拖动效果
16lz
2021-01-23
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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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)); }}}
.[代码]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 loopspublic class FlingGallery extends FrameLayout{// Constantsprivate 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;}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom){super.onLayout(changed, left, top, right, bottom);// Calculate our view widthmGalleryWidth = 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 widthint offsetWidth = mGalleryWidth + mViewPaddingWidth;// Position the previous view one measured width to leftif (viewNumber == getPrevViewNumber(relativeViewNumber)){return offsetWidth;}// Position the next view one measured width to the rightif (viewNumber == getNextViewNumber(relativeViewNumber)){return offsetWidth * -1;}return 0;}void movePrevious(){// Slide to previous viewmFlingDirection = 1;processGesture();}void moveNext(){// Slide to next viewmFlingDirection = -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 recyclenewViewNumber = 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 recyclenewViewNumber = getNextViewNumber(mCurrentViewNumber);mCurrentPosition = getNextPosition(mCurrentPosition);reloadViewNumber = getPrevViewNumber(mCurrentViewNumber);reloadPosition = getNextPosition(mCurrentPosition);}}if (newViewNumber != mCurrentViewNumber){mCurrentViewNumber = newViewNumber; // Reload outgoing view from adapter in new positionmViews[reloadViewNumber].recycleView(reloadPosition);}// Ensure input focus on the current viewmViews[mCurrentViewNumber].requestFocus();// Run the slide animations for view transitionsmAnimation.prepareAnimation(mCurrentViewNumber);this.startAnimation(mAnimation);// Reset fling statemFlingDirection = 0;}void processScrollSnap(){// Snap to next view if scrolled passed snap positionfloat rollEdgeWidth = mGalleryWidth * mSnapBorderRatio;int rollOffset = mGalleryWidth - (int) rollEdgeWidth;int currentOffset = mViews[mCurrentViewNumber].getCurrentOffset();if (currentOffset <= rollOffset * -1){// Snap to previous viewmFlingDirection = 1;}if (currentOffset >= rollOffset){// Snap to next viewmFlingDirection = -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 gallerymInvalidLayout = new FrameLayout(mContext);mInvalidLayout.setLayoutParams(new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));// Internal layout is permanent for durationmInternalLayout = 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 viewmInternalLayout.scrollTo(getViewOffset(mViewNumber, relativeViewNumber) + xOffset, yOffset);}public int getCurrentOffset(){// Return the current scroll positionreturn 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 implementationsmInitialOffset = mViews[mRelativeViewNumber].getCurrentOffset();mTargetOffset = getViewOffset(mRelativeViewNumber, mRelativeViewNumber);mTargetDistance = mTargetOffset - mInitialOffset;// Configure base animation propertiesthis.setDuration(mAnimationDuration);this.setInterpolator(mDecelerateInterpolater);// Start/continued animationmIsAnimationInProgres = 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-screenif ((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 targetmIsAnimationInProgres = 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; } }}
更多相关文章
- Android在代码中设置控件的drawableLeft,drawableRight,drawableTo
- Android随笔--短小精悍的代码段(持续更新)
- 随想录(一个android原生app的代码赏析)
- Loader和Fragment使用示例代码
- Eclipse中查看Android源代码
- Android 记忆卡片游戏 记忆力 Android游戏 Android记忆卡片游戏
- android 源代码在线查看和索引
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析(3)