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>


相关文章
|
5天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
6天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
19 5
|
14天前
|
缓存 数据处理 Android开发
在 Android 中使用 RxJava 更新 View
【10月更文挑战第20天】使用 RxJava 来更新 View 可以提供更优雅、更高效的解决方案。通过合理地运用操作符和订阅机制,我们能够轻松地处理异步数据并在主线程中进行 View 的更新。在实际应用中,需要根据具体情况进行灵活运用,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在 Android 中使用 RxJava 更新 View 的技巧和方法,为开发高质量的 Android 应用提供有力支持。
|
14天前
|
缓存 调度 Android开发
Android 在子线程更新 View
【10月更文挑战第21天】在 Android 开发中,虽然不能直接在子线程更新 View,但通过使用 Handler、AsyncTask 或 RxJava 等方法,可以实现子线程操作并在主线程更新 View 的目的。在实际应用中,需要根据具体情况选择合适的方法,并注意相关的注意事项和性能优化,以确保应用的稳定性和流畅性。可以通过不断的实践和探索,进一步掌握在子线程更新 View 的技巧和方法,为开发高质量的 Android 应用提供支持。
21 2
|
15天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
16天前
|
存储 前端开发 测试技术
Android kotlin MVVM 架构简单示例入门
Android kotlin MVVM 架构简单示例入门
22 1
|
18天前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
19 2
|
缓存 Java Android开发
|
8天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
5天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!