Android实现浮层的上下滑动(支持内部加入View)

前言

我K。今天竟然是情人节。对于资深的单身狗来说,简直是个噩耗,今天注定是各种秀恩爱。心塞中。。

。。

话题到此结束,管他什么情人节,今天给大家带来的是一个浮层的上下滑动,浮层滑动时分三种状态:所有显示、显

示一半、隐藏。可在浮层中加入ListView,GirdView,ImageView等等View。

详细的效果看以下的GIF图:

效果解说

1、在上面的浮层中我们能够看到存放着一个ListView。并能进行上下滚动,也就是说浮层的Touch事件须要在适

当的时候进行拦截,不传递给子View。

这时须要重写onInterceptTouchEvent()和onTouch()方法。

onInterceptTouchEvent和onTouch的介绍请參看以下:

1、onInterceptTouchEvent()是用于处理事件(类似于预处理。当然也能够不处理)并改变事件的传递方向,也就是

决定是否同意Touch事件继续向下(子控件)传递。一但返回True(代表事件在当前的viewGroup中会被处理),则向

下传递之路被截断(全部子控件将没有机会參与Touch事件),同一时候把事件传递给当前的控件的onTouchEvent()处

理;返回false。则把事件交给子控件的onInterceptTouchEvent()。

2、onTouchEvent()用于处理事件,返回值决定当前控件是否消费(consume)了这个事件。也就是说在当前控件在处

理完Touch事件后,是否还同意Touch事件继续向上(父控件)传递,一但返回True,则父控件不用担心自己来处理

Touch事件。返回true,则向上传递给父控件(注:可能你会认为是否消费了有关系吗。反正我已经针对事件编写了

处理代码?答案是有差别。比方ACTION_MOVE或者ACTION_UP发生的前提是一定以前发生了ACTION_DOWN,假设你没有

消费ACTION_DOWN,那么系统会觉得ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。

2、在上图中能够看出。当进行上下滚动时具有滚动效果,能够通过下面代码实现:

ObjectAnimator anim=ObjectAnimator.ofFloat(this, "translationY", values);

实现原理

实现的原理事实上非常easy,我们所要做的就是在onInterceptTouchEvent和onTouch两个事件处理的方法中进行处理。

onInterceptTouchEvent的任务是推断何时拦截事件。交由onTouch处理。何时由子View进行事件处理。

依据上图能够非常清楚知道onInterceptTouchEvent所要做的工作。在onTuch的中最基本的操作是在获取滑动时的

MotionEvent.ACTION_MOVE中进行动画的处理。

到此为止仅仅说讲解了两个事件处理方法该做写什么。在图中能够看出。滑动时主要有三种状态。各自是所有显示

、显示一半、隐藏。

所有显示:当浮层所有显示时。这时浮层视图不应该继续向上滚动,这时须要在onInterceptTouchEvent中将事

件传递给它的子View进行处理;当向下滑动时,须要进行向下移动的动画处理,滑动一次。这时的浮层视图应该显示

一半。

显示一半:当浮层显示一半时。这时进行向上滑动时须要运行向上移动的动画处理,向下滑动时也一样。进行移

动的动画处理。进行隐藏浮层。

隐藏:当浮层进行隐藏时。能够通过点击对应的点击事件,使浮层从底部向上移动,并移动一半。

代码

以上是大体的思路。详细实现请參看以下的代码。

public class FloatingLayerView extends LinearLayout implements OnTouchListener {

	/**
	 * 视图显示类型。

*/
	private int type=ALL;
	/**
	 * 浮层的高度。
	 */
	private int floating_height;
	/**
	 * 浮层的宽度
	 */
	private int floating_width;

	/**
	 * 滑动高度
	 */
	private float move_height;

	/**
	 * 是否向下滑动。交由onTouch事件处理。

*/
	private boolean isCanHide=false;
	/**
	 * 是否进行动画
	 */
	private boolean isCanAnimation=false;

	/**
	 * 触发拦截触摸事件时的坐标点。
	 * 按下:
	 *  interceptTouch_X:按下时的X坐标点。
	 *  interceptTouch_Y:按下时的Y坐标点。

* 滑动:
	 * interceptMove_X:滑动时的X坐标点。
	 * interceptMove_Y:滑动时的Y坐标点。
	 * 距离:
	 * interceptTouch_Move_X:从按下到滑动之间的距离(横向滑动)
	 * interceptTouch_Move_Y:从按下到滑动之间的距离(纵向滑动)
	 * 滑动距离:
	 * moveLength:依据此值推断是否进行了滑动。

*/
	private float interceptTouch_X;
	private float interceptTouch_Y;
	private float interceptMove_X;
	private float interceptMove_Y;
	private float interceptTouch_Move_X;
	private float interceptTouch_Move_Y;
	private int moveLength=10;

	/**
	 * 触发触摸事件时的坐标点
	 * down_X:按下时的X坐标点。
	 * down_Y:按下时的Y坐标点。

* move_X:移动时的X坐标点。

* move_Y:移动时的Y坐标点。

* down_move_X:横向滑动的距离。
	 * down_move_Y:纵向滑动的距离。
	 */
	private float down_X;
	private float down_Y;
	private float move_X;
	private float move_Y;
	private float down_move_X;
	private float down_move_Y;

	/**
	 * 定义三种浮层显示类型
	 * 0:不显示 1:显示一半 2:所有显示
	 */
	private static final int NONE=0;
	private static final int HALF=1;
	private static final int ALL=2;

	public FloatingLayerView(Context context, AttributeSet attrs) {
		super(context, attrs);
		setOnTouchListener(this);
	}

	public FloatingLayerView(Context context) {
		super(context);
	}

	@Override
	public void onWindowFocusChanged(boolean hasWindowFocus) {
		if(hasWindowFocus){
			floating_width=getWidth();
			floating_height=getHeight();
			/**
			 * 每次滑动的距离是当前View宽度的三分之中的一个。
			 */
			move_height=floating_height/3;
		}
		super.onWindowFocusChanged(hasWindowFocus);
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		/**
		 * 当按下时获取x,y的坐标点。
		 */
		case MotionEvent.ACTION_DOWN:
			interceptTouch_X = ev.getX();
			interceptTouch_Y = ev.getY();
			isCanAnimation=true;
			break;
			/**
			 * 当滑动时操作例如以下:
			 * 1、获取滑动距离
			 * 2、推断向上滑动还是向下滑动
			 * 3、向上滑动时
			 * 4、向下滑动时,推断当前显示方式:
			 * (1)、显示一半时。交由onTouch事件处理。
			 * (2)、所有显示时,是否向下滑动交由当前View的子View处理,
			 * 是否交由onTouch事件处理。

*/
		case MotionEvent.ACTION_MOVE:
			interceptMove_X = ev.getX();
			interceptMove_Y = ev.getY();
			interceptTouch_Move_X = Math
					.abs(interceptTouch_X - interceptMove_X);
			interceptTouch_Move_Y = Math
					.abs(interceptTouch_Y - interceptMove_Y);
			/**
			 * 向下滑动
			 */
			if(interceptMove_Y>interceptTouch_Y&&interceptTouch_Move_Y>moveLength&&interceptTouch_Move_Y>interceptTouch_Move_X){
				return isDounTransferOnTouch();
			}
			/**
			 * 向上滑动
			 */
			if(interceptTouch_Y>interceptMove_Y&&interceptTouch_Move_Y>moveLength&&interceptTouch_Move_Y>interceptTouch_Move_X){
				return isUpTransferOnTouch();
			}
			break;
		case MotionEvent.ACTION_UP:
			break;
		default:
			break;
		}
		return super.onInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			break;
			/**
			 * 当滑动时动画操作
			 */
		case MotionEvent.ACTION_MOVE:
			down_X=interceptTouch_X;
			down_Y=interceptTouch_Y;
			move_X=event.getX();
			move_Y=event.getY();
			down_move_X=Math.abs(down_X-move_X);
			down_move_Y=Math.abs(down_Y-move_Y);
			/**
			 * 向下滑动
			 */
			if(move_Y>down_Y&&down_move_Y>moveLength&&getCanAnimation()){
				downAnimationConfig();
			}
			/**
			 * 向上滑动
			 */
			if(down_Y>move_Y&&down_move_Y>moveLength&&getCanAnimation()){
				upAnimationConfig();
			}
			/**
			 * 运行完上面动画处理后,停止运行动画
			 */
			setCanAnimation(false);
			break;
		case MotionEvent.ACTION_UP:
			break;
		default:
			break;
		}
		return true;
	}

	/**
	 * 是否进行动画处理
	 * @return true:处理
	 */
	private boolean getCanAnimation(){
		return isCanAnimation;
	}

	/**
	 * 获取当前视图显示类型
	 * @return
	 */
	private int getType(){
		return type;
	}

	private void setType(int type){
		this.type=type;
	}

	/**
	 * 设置是否进行动画处理
	 * @param canAnimation
	 */
	private void setCanAnimation(boolean canAnimation){
		this.isCanAnimation=canAnimation;
	}

	/**
	 * 向下滑动时的动画处理
	 */
	private void downAnimationConfig(){
		switch (getType()) {
		case HALF://当视图显示一半时
			half2None();
			break;
		case ALL://当视图所有显示时
			all2Half();
			break;

		default:
			break;
		}
	}

	/**
	 * 向上滑动时的动画处理
	 */
	private void upAnimationConfig(){
		switch (getType()) {
		case HALF://当视图显示一半时
			half2All();
			break;
		case ALL://当视图所有显示时
			/**
			 * 当视图已经完整显示,再往
			 * 上滑动也就没不论什么意义进行
			 * 动画处理。
			 */
			break;
		default:
			break;
		}
	}

	/**
	 * 向下滑动时是否交由onTouch事件处理
	 * @return true:由onTouch事件处理,不传递给子View
	 */
	private boolean isDounTransferOnTouch(){
		switch (type) {
		case NONE:
			break;
		case HALF:
			return true;
		case ALL:
			if(isCanHide){
				return true;
			}
			break;
		default:
			break;
		}
		return false;
	}

	/**
	 * 向上滑动时是否交由onTouch事件处理
	 * @return true:由onTouch事件处理。不传递给子View
	 */
	private boolean isUpTransferOnTouch(){
		switch (type) {
		case NONE:
			break;
		case HALF:
			return true;
		case ALL:
			break;
		default:
			break;
		}
		return false;
	}

	/**
	 * 当向下滑动时,当前视图显示一半,再往下滑动隐藏。

* type设置为NONE
	 */
	private void half2None(){
		float[] values=new float[]{move_height,getHeight()};
		startAnimation(values);
		setType(NONE);
	}

	/**
	 * 当向下滑动时。当前视图显示完整,再往下滑动视图显示一半。
	 * type设置为HALF
	 */
	private void all2Half(){
		float[] values=new float[]{0,move_height};
		startAnimation(values);
		setType(HALF);
	}

	/**
	 * 当向上滑动时,当前视图显示一半,再往上滑动,视图显示完整。
	 * type设置为ALL
	 */
	private void half2All(){
		float[] values=new float[]{move_height,0};
		startAnimation(values);
		setType(ALL);
	}

	/**
	 * 运行动画
	 * @param values
	 */
	private void startAnimation(float[] values){
		AnimatorSet as=new AnimatorSet();
		ObjectAnimator anim=ObjectAnimator.ofFloat(this, "translationY", values);
		anim.setDuration(500);
		as.playTogether(anim);
		as.start();
	}

	/**
	 * 当前视图显示完整时的动画处理
	 */
	private void all2None(){
		float[] values=new float[]{0,getHeight()};
		startAnimation(values);
		setType(HALF);
	}

	/**
	 * 隐藏浮层
	 */
	public void beforeInput(){
		switch (getType()) {
		case NONE:
			break;
		case HALF:
			half2None();
			break;
		case ALL:
			all2None();
			break;

		default:
			break;
		}
	}

	/**
	 * 显示浮层一半
	 */
	public void none2Half(){
		float[] values=new float[]{getHeight(),move_height};
		startAnimation(values);
		setType(HALF);
	}

	/**
	 * 显示所有浮层
	 */
	public void none2All(){
		float[] values=new float[]{getHeight(),0};
		startAnimation(values);
		setType(HALF);
	}

	/**
	 * 是否进行动画滚动
	 * @param canHide
	 */
	public void setCanHide(boolean canHide){
		this.isCanHide=canHide;
	}

}

在代码中已经进行了非常具体的凝视。在代码的最后暴露了几个可调用的方法。能够通过这几个方法实现我们的滑动效

果。

在Activity中的通过GridView的OnScrollListener监听事件进行推断何时进行动画的滚动。何时停止,当然在FloatingLa

yerView能够加上想要加的View。比例如以下面在FloatingLayerView中加入了GirdView:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white" >

    <Button
        android:id="@+id/btn_show"
        android:layout_width="fill_parent"
        android:layout_height="50dp"
        android:text="显示浮层" />

    <Button
        android:id="@+id/btn_hide"
        android:layout_width="fill_parent"
        android:layout_height="50dp"
        android:layout_below="@id/btn_show"
        android:text="隐藏浮层" />

    <!-- 覆盖层 -->

    <com.example.floatinglayeranimtion.FloatingLayerView
        android:id="@+id/activity_shine_ll_cover"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/btn_hide"
        android:layout_marginTop="40dp"
        android:background="@android:color/holo_blue_dark"
        android:visibility="visible" >

        <GridView
            android:id="@+id/activity_shine_gv_all"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:layout_marginTop="5dp"
            android:horizontalSpacing="4dp"
            android:listSelector="@null"
            android:numColumns="4"
            android:verticalSpacing="4dp" >
        </GridView>
    </com.example.floatinglayeranimtion.FloatingLayerView>

</RelativeLayout>

Activity的代码例如以下:

public class MainActivity extends Activity implements OnClickListener {

	private Button btn_show;
	private Button btn_hide;
	private GridView gv_all;

	private TestAdapter testAdapter = new TestAdapter();

	// 覆盖层
	private FloatingLayerView mFloatingLayerView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
		addListener();
	}

	private void initView() {
		btn_show = (Button) findViewById(R.id.btn_show);
		btn_hide = (Button) findViewById(R.id.btn_hide);
		// 覆盖层
		mFloatingLayerView = (FloatingLayerView) findViewById(R.id.activity_shine_ll_cover);
		gv_all = (GridView) findViewById(R.id.activity_shine_gv_all);

		gv_all.setAdapter(testAdapter);

	}

	private void addListener() {
		btn_show.setOnClickListener(this);
		btn_hide.setOnClickListener(this);
		gv_all.setOnScrollListener(scrollListener);

	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {

		// 显示浮层
		case R.id.btn_show:
			mFloatingLayerView.none2Half();
			break;

		// 隐藏浮层
		case R.id.btn_hide:
			mFloatingLayerView.beforeInput();
			break;

		}
	}

	/** 覆盖层中GridView滑动监听 */
	private OnScrollListener scrollListener = new OnScrollListener() {

		@Override
		public void onScrollStateChanged(AbsListView view, int scrollState) {
		}

		@Override
		public void onScroll(AbsListView view, int firstVisibleItem,
				int visibleItemCount, int totalItemCount) {
			if (firstVisibleItem == 0) {
				mFloatingLayerView.setCanHide(true);
			} else {
				mFloatingLayerView.setCanHide(false);
			}

		}

	};

	// =============測试======================
	private int[] images = new int[] { R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher, R.drawable.ic_launcher, R.drawable.ic_launcher,
			R.drawable.ic_launcher };

	class TestAdapter extends BaseAdapter {

		@Override
		public int getCount() {
			return images.length;
		}

		@Override
		public Object getItem(int position) {
			return images[position];
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			View view = LayoutInflater.from(MainActivity.this).inflate(
					R.layout.image, null);

			ImageView imagView = (ImageView) view.findViewById(R.id.iv_show);
			imagView.setBackgroundResource(images[position]);
			return view;
		}

	}

}

OK,至此滑动的浮层已经实现了,欢迎大家吐槽。

-------------------------------------------------------------------------------------------------------------------------------------------------------

转载请注明出处:http://blog.csdn.net/hai_qing_xu_kong/article/details/47806881情绪控

项目GitHub地址:https://github.com/LinhaiGu/FloatingLayerAnimtion

时间: 2024-10-09 11:09:20

Android实现浮层的上下滑动(支持内部加入View)的相关文章

Android实现浮层的上下滑动(支持内部添加View)

前言 我K,今天居然是情人节,对于资深的单身狗来说,简直是个噩耗,今天注定是各种秀恩爱,心塞中.... 话题到此结束,管他什么情人节,今天给大家带来的是一个浮层的上下滑动,浮层滑动时分三种状态:全部显示.显 示一半.隐藏.可在浮层中添加ListView,GirdView,ImageView等等View. 具体的效果看下面的GIF图: 效果讲解 1.在上面的浮层中我们可以看到存放着一个ListView,并能进行上下滚动,也就是说浮层的Touch事件需要在适 当的时候进行拦截,不传递给子View.这

Android 尺子效果,根据时间支持左右滑动(原创)

过完年,到现在也没有认真的写代码,正好公司有需求,花了一个下午,写了一个尺子效果的自定义View,效果还行,但是还有很多需要改进的地方,希望大家指正!!!可以左右滑动,下面是关键代码!! package com.example.drawrulerview; import OnCustomRurleListener.OnCustomViewListener; import android.annotation.SuppressLint; import android.content.Context

Android官方入门文档[9]支持不同的语言

Android官方入门文档[9]支持不同的语言 Supporting Different Languages支持不同的语言 This class teaches you to1.Create Locale Directories and String Files2.Use the String Resources You should also read?Localization Checklist?Localization with Resources 该课程教你1.创建区域设置目录和文件的字

Android 实现用户列表信息滑动删除功能和选择删除功能

在项目开发过程中,常常需要对用户列表的信息进行删除的操作.Android中常用的删除操作方式有两种 ,一种就是类似微信的滑动出现删除按钮方式,还有一种是通过CheckBox进行选择,然后通过按钮进行删除的方式.本来的实例集成上述的两种操作方式来实现用户列表删除的效果. 设计思路:在适配器类MyAdapter一个滑动删除按钮显示或隐藏的Map,一个用于CheckBox是否选中的Map和一个与MainAcitivyt进行数据交互的接口ContentsDeleteListener,同时该接口包含两个方

android圆形旋转菜单,并支持移动换位功能

LZ最近接手公司一个项目,需要写一个圆形的旋转菜单,并且支持菜单之间的移动换位,本来以为这种demo应该网上是很多的,想不到度娘也是帮不了我,空有旋转功能但是却不能换位置,所以LZ就只能靠自己摸索了. 最终LZ参考了网上的部分代码,重写了一个自定义的view终于实现了这个看似很吊,却没有实际意义的功能.在此贡献出来给广大码农们共享. 话不多说,先上代码: 自定义view类: public class RoundSpinView extends View { private Paint mPain

Android开发之bindService()侦听service内部状态

在Android开发之bindService()通信的基础上,实现bindService()方法侦听service内部状态. 实现侦听service内部状态,使用的是回调机制 1.首先实现一个接口 1 public static interface CallBack{ 2 void onDataChange(String data); 3 } 2. 1 private CallBack callBack=null; 2 public void setCallBack(CallBack callB

Android中监听ScrollView滑动停止和滑动到底部

1.监听ScrollView滑动停止: [java] view plaincopy /********************监听ScrollView滑动停止*****************************/ scrollView.setOnTouchListener(new OnTouchListener() { private int lastY = 0; private int touchEventId = -9983761; Handler handler = new Hand

Android如何提高ListView的滑动效率

如何提高ListView的滚动速度,ListView的滚动速度的提高在于getView方法的实现,通常我们的getView方法会这样写: [java] view plaincopy View getView(int position,View convertView,ViewGroup parent){ //首先构建LayoutInflater LayoutInflater factory = LayoutInflater.from(context); View view = factory.i

Android开发调试日志工具类[支持保存到SD卡]

直接上代码: package com.example.callstatus; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.net.UnknownHostException; import java.text.SimpleDateFormat; impor