Android自定义View示例(三)—滑动控件

简介: MainActivity如下: package cc.testview4;import cc.testview4.SlideView.SwitchChangedListener;import android.

MainActivity如下:

package cc.testview4;
import cc.testview4.SlideView.SwitchChangedListener;
import android.app.Activity;
import android.os.Bundle;
/**
 * Demo描述:
 * 自定义滑动控件
 * 
 * 参考资料:
 * http://blog.csdn.net/lfdfhl/article/details/8195441
 * 
 * 备注说明:
 * 在CopyOfSlideView中使用了另外一种方式:
 * 主要涉及到自定义控件的Touch和GestureDetector的处理
 * 详细代码请参见SlideView是一种很好的方式和思路!!!
 * 该方式中有一点点点(很微小)BUG参见其中代码的第71行
 */
public class MainActivity extends Activity {
	private SlideView mSlideView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		init();
	}
	
	private void init(){
		mSlideView=(SlideView) findViewById(R.id.slideView);
		mSlideView.setSwitchChangedListener(new SwitchChangedListenerImpl());
	}
	
	private class SwitchChangedListenerImpl implements SwitchChangedListener{
		@Override
		public void OnChanged(String info) {
			System.out.println("info="+info);
		}
		
	}
}


SlideView如下:

package cc.testview4;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;

public class SlideView extends RelativeLayout{
	private SwitchChangedListener mSwitchChangedListener;
	private Context mContext;
	private View mSlideView;
	private int dotRawLeft;
	private int dotRawRight;
	private int dotRawTop;
	private int dotRawBottom;
	
	private int downX;
	private int downY;
	private int dotCurrentLeft;
	private int dotCurrentRight;
	private int dotCurrentTop;
	private int dotCurrentBottom;
	
	// 中间圆球
    private ImageView mDotImageView;
	// 上一步
	private ImageView mPreStepImageView;
	// 下一步
	private ImageView mNextStepImageView;
	// 上一步箭头线
	private ImageView mPreArrowImageView;
	// 下一步箭头线
	private ImageView mNextArrowImageView;
	// 上一步箭头的左边相对于parent左边的位置
	private int preArrowLeft;
	private int preArrowWidth;
	// 上一步箭头的右边相对于parent左边的位置
	private int preArrowRight;
	// 下一步箭头的左边相对于parent左边的位置
	private int nextArrowLeft;
	// 下一步箭头的右边相对于parent左边的位置
	private int nextArrowWidth;
	private int nextArrowRight;
	
	public SlideView(Context context) {
		super(context);		
		init(context);
	}

	public SlideView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}
	
	private void init(Context context){
		mContext=context;
		LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		mSlideView = inflater.inflate(R.layout.slideview, this);

		mDotImageView = (ImageView) mSlideView.findViewById(R.id.dotImageView);
		mPreStepImageView = (ImageView) mSlideView.findViewById(R.id.preStepImageView);
		mNextStepImageView = (ImageView) mSlideView.findViewById(R.id.nextStepImageView);
		mNextArrowImageView = (ImageView) mSlideView.findViewById(R.id.nextArrowImageView);
		mPreArrowImageView = (ImageView) mSlideView.findViewById(R.id.preArrowImageView);
		
		this.getRawLocation();

		// 自定义组件添加触摸监听事件
		mDotImageView.setOnTouchListener(new TouchListenerImpl());
	}
	
	//利用Post方式获取一些位置坐标
	private void getRawLocation(){
		mNextArrowImageView.post(new Runnable() {
			@Override
			public void run() {
				dotRawLeft = mDotImageView.getLeft();
				dotRawRight = mDotImageView.getRight();
				dotRawTop = mDotImageView.getTop();
				dotRawBottom = mDotImageView.getBottom();
				
				preArrowWidth=mPreArrowImageView.getWidth();
				nextArrowWidth=mNextArrowImageView.getWidth();
			}
		});
	}
	
	private class TouchListenerImpl implements OnTouchListener{
		@Override
		public boolean onTouch(View view, MotionEvent event) {
			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				downX = (int) event.getRawX();
				downY = (int) event.getRawY();	
				break;
			case MotionEvent.ACTION_MOVE:
				//手指向左滑动,dx<0
				//手指向右滑动,dx>0
				int distanceX =(int)event.getRawX() - downX;//移动的距离
				int distanceY =(int)event.getRawY() - downY;//移动的距离
				
				//防止越界
				if (distanceX<0) {
					if (Math.abs(distanceX)>=preArrowWidth) {
						distanceX=-preArrowWidth;
					}
				} else {
					if (distanceX>=nextArrowWidth) {
						distanceX=nextArrowWidth;
					}
				}
				
				dotCurrentLeft= dotRawLeft + distanceX;
				dotCurrentRight = dotRawRight + distanceX;
				
				System.out.println("dotCurrentLeft="+dotCurrentLeft+",dotCurrentRight="+dotCurrentRight);
				
				if (distanceX<0&&Math.abs(distanceX)==preArrowWidth) {
					mSwitchChangedListener.OnChanged("上一步");
				}
				if (distanceX>0&&distanceX==nextArrowWidth) {
					mSwitchChangedListener.OnChanged("下一步");
				}
				
				mDotImageView.layout(dotCurrentLeft, dotRawTop, dotCurrentRight,dotRawBottom);
				
			    break;
			case MotionEvent.ACTION_UP:
				mDotImageView.layout(dotRawLeft, dotRawTop, dotRawRight,dotRawBottom);
				break;
			default:
				break;
			}
			
			return true;
		}
   }
	
	
	
	
	// 回调接口
	public interface SwitchChangedListener {
		public void OnChanged(String info);
	}

	public void setSwitchChangedListener(SwitchChangedListener switchChangedListener) {
        this.mSwitchChangedListener=switchChangedListener;
	}

}

CopyOfSlideView如下:

package cc.testview4;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;

public class CopyOfSlideView extends RelativeLayout{
	private SwitchChangedListener mSwitchChangedListener;
	private Context mContext;
	private View mSlideView;
	private int dotRawLeft;
	private int dotRawRight;
	private int dotRawTop;
	private int dotRawBottom;
	// 中间圆球
    private ImageView mDotImageView;
	// 上一步
	private ImageView mPreStepImageView;
	// 下一步
	private ImageView mNextStepImageView;
	// 上一步箭头线
	private ImageView mPreArrowImageView;
	// 下一步箭头线
	private ImageView mNextArrowImageView;
	// 上一步箭头的左边相对于parent左边的位置
	private int preArrowLeft;
	// 上一步箭头的右边相对于parent左边的位置
	private int preArrowRight;
	// 下一步箭头的左边相对于parent左边的位置
	private int nextArrowLeft;
	// 下一步箭头的右边相对于parent左边的位置
	private int nextArrowRight;
	
	// 在X轴上一共移动的距离
	private int sliddingSumX = 0;
	private RelativeLayout mSlideViewRelativeLayout;
	private GestureDetector mGestureDetector;
	private SlideViewGestureListener mSlideViewGestureListener;
	public CopyOfSlideView(Context context) {
		super(context);		
		init(context);
	}

	public CopyOfSlideView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}
	
	private void init(Context context){
		mContext=context;
		LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		mSlideView = inflater.inflate(R.layout.slideview, this);

		mDotImageView = (ImageView) mSlideView.findViewById(R.id.dotImageView);
		mPreStepImageView = (ImageView) mSlideView.findViewById(R.id.preStepImageView);
		mNextStepImageView = (ImageView) mSlideView.findViewById(R.id.nextStepImageView);
		mNextArrowImageView = (ImageView) mSlideView.findViewById(R.id.nextArrowImageView);
		mPreArrowImageView = (ImageView) mSlideView.findViewById(R.id.preArrowImageView);
		mSlideViewRelativeLayout = (RelativeLayout) findViewById(R.id.slideView_RelativeLayout);
		
		this.getRawLocation();
		
		mSlideViewGestureListener = new SlideViewGestureListener();
		mGestureDetector = new GestureDetector(mContext,mSlideViewGestureListener);

		// 自定义组件添加触摸监听事件
		// 利用以下一句是对整个自定义控件实现了Touch监听,滑块滑动时正常
		mSlideView.setOnTouchListener(new TouchListenerImpl());
		// 利用以下一句是对滑块实现了Touch监听,滑块滑动时有抖动现象,原因不明待查
		//mDotImageView.setOnTouchListener(new TouchListenerImpl());
	}
	
	//利用Post方式获取一些位置坐标
	private void getRawLocation(){
		mNextArrowImageView.post(new Runnable() {
			@Override
			public void run() {
				dotRawLeft = mDotImageView.getLeft();
				dotRawRight = mDotImageView.getRight();
				dotRawTop = mDotImageView.getTop();
				dotRawBottom = mDotImageView.getBottom();
				preArrowLeft = mPreArrowImageView.getLeft();
				preArrowRight = mPreArrowImageView.getRight();
				nextArrowLeft = mNextArrowImageView.getLeft();
				nextArrowRight = mNextArrowImageView.getRight();
			}
		});
	}
	
	private class TouchListenerImpl implements OnTouchListener{
		@Override
		public boolean onTouch(View view, MotionEvent event) {
			switch (event.getAction()) {
			case MotionEvent.ACTION_UP:
				// 滑动距离小于五分之四,回到原位
				if (Math.abs(sliddingSumX) < (preArrowRight - preArrowLeft) * 4 / 5) {
					mDotImageView.layout(dotRawLeft, dotRawTop, dotRawRight,dotRawBottom);
					sliddingSumX = 0;
				} else {
					if (mDotImageView.getLeft() < dotRawLeft) {
						Toast.makeText(mContext, "上一步", 0).show();
						mSwitchChangedListener.OnChanged("到达了上一步");
						mDotImageView.layout(dotRawLeft, dotRawTop,dotRawRight, dotRawBottom);
						sliddingSumX = 0;
					} else {
						Toast.makeText(mContext, "下一步", 0).show();
						mSwitchChangedListener.OnChanged("到达了下一步");
						mDotImageView.layout(dotRawLeft, dotRawTop,dotRawRight, dotRawBottom);
						sliddingSumX = 0;
					}

				}
				break;
			default:
				break;
			}
			
			/**
			 * 注意:
			 * 在OnTouchListener中把move和down事件处理都转交给了mGestureDetector.
			 * 在OnTouchListener中只处理了up事件---在手指抬起后显示dotImageView.
			 */
			return mGestureDetector.onTouchEvent(event);
		}
   }
	
	
	// 自定义控件的手势
	// 注意:在相应的方法中返回true,表示已经消费完事件
	private class SlideViewGestureListener implements GestureDetector.OnGestureListener {
		@Override
		public boolean onDown(MotionEvent e) {
			return true;
		}

		// 在自定义控件滑动时候执行此方法
		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {
			
			sliddingSumX = sliddingSumX + (int) distanceX;
			// 再次显示dotImageView时只需要改变X轴方向的值
			// 往左拉动,X增大;往右拉动,x减小
			// 注意:往右拉动,distanceX为负值;往左拉动,distanceX为正值.
			// 所以不是:rawLeft+sumX, rawTop, rawRight+sumX, rawBottom
			// 而应该是:rawLeft-sumX, rawTop, rawRight-sumX, rawBottom
			if (dotRawLeft - sliddingSumX >= (preArrowLeft - (mDotImageView.getWidth() * 0.5))&& 
				dotRawRight - sliddingSumX <= nextArrowRight+ ((mDotImageView.getWidth() * 0.5))) {
				System.out.println("==> distanceX="+distanceX+",sliddingSumX="+sliddingSumX);
				mDotImageView.layout
				(dotRawLeft - sliddingSumX, dotRawTop,dotRawRight - sliddingSumX, dotRawBottom);
			} else {
				System.out.println("超过了两头的边界");
			}
			
			return true;
		}

		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {
			return false;
		}

		@Override
		public void onLongPress(MotionEvent e) {
		}

		@Override
		public void onShowPress(MotionEvent e) {
		}

		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			return false;
		}
	}
	
	
	// 回调接口
	public interface SwitchChangedListener {
		public void OnChanged(String info);
	}

	public void setSwitchChangedListener(SwitchChangedListener switchChangedListener) {
        this.mSwitchChangedListener=switchChangedListener;
	}

}


main.xml如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <cc.testview4.SlideView
        android:id="@+id/slideView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
    />

</RelativeLayout>


slideview.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/slideView_RelativeLayout"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:background="@drawable/step_bg"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/dotImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@drawable/step_ball"
        android:clickable="false" />

    <ImageView
        android:id="@+id/preArrowImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_toLeftOf="@id/dotImageView"
        android:background="@drawable/step_preline" />

    <ImageView
        android:id="@+id/preStepImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginRight="10dip"
        android:layout_toLeftOf="@id/preArrowImageView"
        android:background="@drawable/step_pre" />

    <ImageView
        android:id="@+id/nextArrowImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_toRightOf="@id/dotImageView"
        android:background="@drawable/step_nextline" />

    <ImageView
        android:id="@+id/nextStepImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_marginLeft="10dip"
        android:layout_toRightOf="@id/nextArrowImageView"
        android:background="@drawable/step_next" />

</RelativeLayout>


相关文章
|
14天前
|
XML Java Android开发
Android实现自定义进度条(源码+解析)
Android实现自定义进度条(源码+解析)
47 1
|
3月前
|
JavaScript Android开发
使用贝叶斯曲线滑动安卓屏幕(autojsPro7)
使用贝叶斯曲线滑动安卓屏幕(autojsPro7)
60 0
|
3月前
|
存储 算法 开发工具
OpenCV 安卓编程示例:1~6 全
OpenCV 安卓编程示例:1~6 全
50 0
|
3月前
|
Android开发 容器
Android UI设计: 什么是View和ViewGroup?
Android UI设计: 什么是View和ViewGroup?
35 0
|
18天前
|
Android开发
Android 开发 pickerview 自定义选择器
Android 开发 pickerview 自定义选择器
10 0
|
1月前
|
Android开发
[Android]RadioButton控件
[Android]RadioButton控件
12 0
|
3月前
|
Android开发
分享88个Android控件源代码总有一个是你想要的
分享88个Android控件源代码总有一个是你想要的
21 0
|
3月前
|
Android开发
分享89个Android控件源代码总有一个是你想要的
分享89个Android控件源代码总有一个是你想要的
70 0
|
3月前
|
Android开发 Kotlin 索引
Android Compose——ScrollableTabRow和LazyColumn同步滑动
Android Compose——ScrollableTabRow和LazyColumn同步滑动
|
4月前
|
XML API Android开发
Android 自定义View 之 Dialog弹窗
Android 自定义View 之 Dialog弹窗