HorizontalScrollView扩展总结

HorizontalScrollView扩展总结ScrollView相信大家都已经比较熟悉了,它是支持垂直滚动的,在开发中经常使用到,与垂直滚动相对的就是水平滚动HorizontalScrollView,有时我们在进行页面切换的时候也会用到HorizontalScrollView。通过查看源码比较发现ScrollView和HorizontalScrollView有好多相同的方法。在说扩展之前,我先说一下HorizontalScrollVie

大家好,又见面了,我是你们的朋友全栈君。

ScrollView相信大家都已经比较熟悉了,它是支持垂直滚动的,在开发中经常使用到,与垂直滚动相对的就是水平滚动HorizontalScrollView,有时我们在进行页面切换的时候也会用到HorizontalScrollView。通过查看源码比较发现ScrollView和HorizontalScrollView有好多相同的方法。

在说扩展之前,我先说一下HorizontalScrollView的特点

(1) 支持水平滚动

(2) 和ScrollView一样,它只包括一个子View,通常是用LinearLayout作为它的子View,当然还可以用用其它的View

(3) HorizontalScroll内部使用到的OverScroller 缺省滑动的时间为DEFAULT_DURATION = 250 ms

(4) 可以平滑也可以瞬间滑动,平滑则调用smoothScrollBy(int dx,int dy)滑动多少距离)/smoothScrollTo(int x,int y)滑动到x,y位置

     瞬间滑动则调用 scrollBy(int x,int y) scrollTo(int x,int y)

HorizontalScrollView 与滚动有关的常用方法

public final void smoothScrollBy(int dx, int dy)
public final void smoothScrollTo(int x, int y)
public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled)
public void scrollTo(int x, int y) 覆写了父类View的scrollTo

先看 setSmoothScrollingEnabled 这个方法,设置是否有平滑滚动效果,此方法是设置一个标记,此标记会被HorizontalScrollView的

如下方法间接使用到:

public boolean executeKeyEvent(KeyEvent event)
public boolean fullScroll(int direction)
public boolean pageScroll(int direction)
public boolean arrowScroll(int direction)
protected void onSizeChanged(int w, int h, int oldw, int oldh)

如上方法会出发滚动,而滚动是否有平滑效果则取决于setSmoothScrollingEnabled 方法设置的标记。
onSizeChanged方法是当HorizontalScrollView的大小发生改变的时候触发调用的;
标记具体被使用的过程如下:

setSmoothScrollingEnabled(boolean smoothScrollingEnabled)方法 设置的mSmoothScrollingEnabled标记只在doScrollX(int delta)有使用到

在doScrollX内部如果mSmoothScrollingEnabled是true则会调用public smoothScrollBy否则调用scrollBy

而 doScrollX方法被调用关系如下:

public boolean executeKeyEvent(KeyEvent event) -> fullScroll(),pageScroll(),arrowScroll()
public boolean fullScroll(int direction) -> private scrollAndFocus() -->private doScrollX()
public boolean pageScroll(int direction) -> private scrollAndFocus() -->private doScrollX()
public boolean arrowScroll(int direction) -> doScrollX(int delta)
protected void onSizeChanged(int w, int h, int oldw, int oldh) -> doScrollX()

知道了HorizontalScrollView这些特点之后,刚好项目中有这样一个需求:注册模块,

要求:

(1) 划分3个步骤,每个步骤页面是不一样的

(2) 步骤可以回退

(3) 每个页面只初始化一次,

(4) 不使用三个Activity

排除ViewPager,Activity,Fragment,这时可以使用HorizontalScrollView通过滚动来实现,那么就需要扩展HorizontalScrollView了。

此扩展HorizontalScrollView有如下特点:

(1) 可禁用手势滑动,只能通过调用scrollBy,scrollTo,smoothScrollBy, smoothScrollTo来滑动(因为每个步骤切换是通过点击下一步,而不能手势滑动)

(2) 也支持手势滑动

(3) 支持滑动的监听(滑动动作完成后才去更新步骤状态)

主要实现过程:

(1)  继承HorizontalScrollView

(2)  增加自定义方法public void enableTouchScroll(boolean enAbleTouchScroll) 是否允许手势触摸滑动

(3)  覆写public boolean onTouchEvent(MotionEvent ev) 如果支持手势滑动,如果有设置滚动监听则监听滚动,同时调用父类HorizontalScroll的onTouch;

     如果不支持手势滑动,则直接return true直接将touch事件交给子View进行处理

(4) 增加自定义方法public void setOnScrollStateChangedListener(ScrollViewListener listener,Handler handler) 设置滚动监听,这里handler是用于发送消息(每隔多少ms去获取一次滚  动的距离从而知道是否滚动)

(5)增加自定义方法public final void smoothScrollByExt(int dx, int dy)和public final void smoothScrollToExt(int x, int y)支持滚动监听

(6)增加自定义方法public boolean isFinishedScroll() 滚动是否完成(内部是通过调用HorizontalScroll的OverScroll对象的isFinished方法,而OverScroll对象是通过反射得到)

注意以上滚动监听只有设置了滚动监听且调用了smoothScrollByExt或smoothScrollToExt方法或者支持手势滚动才有用。

完整源码如下:

package com.nandudu.engsv.widget;

import java.lang.reflect.Field;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;
import android.widget.OverScroller;

/**
 * 水平滚动条
 * (1)可设置是否允许手势触摸滚动(默认是支持手势触摸滚动的) <br>
 * (2)支持滚动状态监听
 * 
 * @author Lue
 * 
 */
public class MyHorizontalScrollView extends HorizontalScrollView
{

	/**
	 * 是否允许手势触摸滚动
	 */
	private boolean enAbleTouchScroll = true;

	private int scrollDealy = 50;

	/**
	 * 滚动状态:停止
	 */
	public final static int SCROLL_STATE_IDLE = 0;

	/**
	 * 滚动状态: 手指拖动滚动
	 */
	public final static int SCROLL_STATE_TOUCH_SCROLL = 1;

	/**
	 * 滚动状态: 正在滑动
	 */
	public final static int SCROLL_STATE_FLING = 2;

	/**
	 * 记录当前滚动的距离
	 */
	private int currentX = -9999999;

	private Handler mHandler;

	private ScrollViewListener scrollViewListener;

	private OverScroller parentMScroller;
	
	/**
	 * 当前滚动状态
	 */
	private int scrollState = SCROLL_STATE_IDLE;

	private String TAG = "MyHorizontalScrollView";

	public interface ScrollViewListener
	{
		/**
		 * @param scrollState 滚动状态 
		 * <br>{@link MyHorizontalScrollView#SCROLL_STATE_IDLE}
		 * <br>{@link MyHorizontalScrollView#SCROLL_STATE_TOUCH_SCROLL}
		 * <br>{@link MyHorizontalScrollView#SCROLL_STATE_FLING}
		 */
		void onScrollChanged(int scrollState);
	}

	/**
	 * 滚动监听runnable
	 */
	private Runnable scrollRunnable = new Runnable()
	{
		@Override
		public void run()
		{

			// TODO Auto-generated method stub
			if (getScrollX() == currentX)
			{
				// 滚动停止
				Log.d(TAG, "停止滚动");
				scrollState = SCROLL_STATE_IDLE;
				if (scrollViewListener != null)
				{
					scrollViewListener.onScrollChanged(scrollState);
				}
				// 取消监听线程
				mHandler.removeCallbacks(this);
				return;
			}
			else
			{
				// 手指离开屏幕 view还在滚动的时候
				Log.d(TAG, "Fling...");
				scrollState = SCROLL_STATE_FLING;
				 if(scrollViewListener!=null)
				 {
				 scrollViewListener.onScrollChanged(scrollState);
				 }
			}

			currentX = getScrollX();

			mHandler.postDelayed(this, scrollDealy);
		}
	};

	public MyHorizontalScrollView(Context context)
	{
		super(context);
		init();
	}

	public MyHorizontalScrollView(Context context, AttributeSet attrs,
			int defStyle)
	{
		super(context, attrs, defStyle);
		init();
	}

	public MyHorizontalScrollView(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		init();
	}
	
	private void init()
	{
		try
		{
			Class<?> type = HorizontalScrollView.class;
			
			Field f = type.getDeclaredField("mScroller");
			f.setAccessible(true);
			
			parentMScroller = (OverScroller)f.get(this);
		}
		
		catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * 是否允许手势触摸滚动
	 * 
	 * @param enAbleTouchScroll
	 *            true 允许 false不允许
	 */
	public void enableTouchScroll(boolean enAbleTouchScroll)
	{
		this.enAbleTouchScroll = enAbleTouchScroll;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev)
	{
		if (enAbleTouchScroll)
		{
			if (mHandler != null)
			{
				switch (ev.getAction())
				{
				case MotionEvent.ACTION_MOVE:
					
					this.scrollState = SCROLL_STATE_TOUCH_SCROLL;
					if(scrollViewListener != null)
					{
						scrollViewListener.onScrollChanged(scrollState);
					 }
					
					// 手指在上面移动的时候 取消滚动监听线程
					mHandler.removeCallbacks(scrollRunnable);
					break;
				case MotionEvent.ACTION_UP:
					// 手指移动的时候
					mHandler.post(scrollRunnable);
					break;
				}
			}

			return super.onTouchEvent(ev);
		}
		else
		{
			return true;
		}
	}

	/**
	 * 对父类HorizontalScrollView方法smoothScrollBy的扩展,增加了滚动监听
	 * 
	 * @see {@link HorizontalScrollView#smoothScrollBy(int, int)}
	 * @param dx
	 * @param dy
	 */
	public final void smoothScrollByExt(int dx, int dy)
	{
		super.smoothScrollBy(dx, dy);

		// 开始监听滑动
		if (mHandler != null)
		{
			mHandler.postDelayed(scrollRunnable, scrollDealy);
		}
	}

	/**
	 * 滚动是否完成
	 * @return
	 */
	@SuppressLint("NewApi")
	public boolean isFinishedScroll()
	{
		if(parentMScroller != null)
		{
			return parentMScroller.isFinished();
		}
		
		return true;
	}
	
	/**
	 * 对父类HorizontalScrollView方法smoothScrollTo的扩展,增加了滚动监听
	 * 
	 * @see {@link HorizontalScrollView#smoothScrollTo(int, int)}
	 * @param x
	 * @param y
	 */
	public final void smoothScrollToExt(int x, int y)
	{
		super.smoothScrollTo(x, y);

		
		// 开始监听滑动
		if (mHandler != null)
		{
			mHandler.postDelayed(scrollRunnable, scrollDealy);
		}
	}

	/**
	 * 必须先调用这个方法设置Handler
	 * 
	 * @param handler
	 */
	private void setHandler(Handler handler)
	{
		this.mHandler = handler;
	}

	/**
	 * 
	 * 设置滚动监听<br>
	 * 此滚动监听在如下情况有效<br>
	 * (1)支持触摸滚动<br>
	 * (2)调用了 smoothScrollByExt/smoothScrollToExt
	 */
	public void setOnScrollStateChangedListener(ScrollViewListener listener,
			Handler handler)
	{
		this.scrollViewListener = listener;
		setHandler(handler);
	}
}

那么具体使用MyHorizontalScrollView的例子如下:


myHorizonScrollView = (MyHorizontalScrollView) findViewById(R.id.myhorslview_calcu);
myHorizonScrollView.enableTouchScroll(false);
		myHorizonScrollView.setOnScrollStateChangedListener(
				new MyHorizontalScrollView.ScrollViewListener()
				{
					@Override
					public void onScrollChanged(int scrollState)
					{
						if (scrollState == MyHorizontalScrollView.SCROLL_STATE_IDLE)
						{//滚动完成后做的事情..Eg:更新步骤状态
							
						}

					}
				}, new Handler());

。。。。
//步骤回退的代码
if (myHorizonScrollView.isFinishedScroll())
{
if (currStep > 0)
			{
				currStep--;
                                //这里每一步的宽度是屏幕宽度,滚动到的位置=当前是哪一步*屏幕宽度(这里currStep是从0开始的)
				myHorizonScrollView.smoothScrollToExt(currStep * displayWidth, 0);
			}
}

。。。。。


//切换到下个步骤的代码
if (currStep < 2)
			{// 滚动到下个页面
				currStep++;
				myHorizonScrollView.smoothScrollToExt(currStep * width, 0);

			}

至此HorizontalScroll的扩展就完成了,

还有一个问题:如果觉得HorizontalScrollView 中使用到的 OverScroller 缺省滑动的时间,DEFAULT_DURATION = 250 ms

这250ms时间太长或太短,那么我的实现思路是这样的:

(1) 重写OverScroller(继承OverScroller)

(2) 覆写OverScroller 的public void startScroll(int startX, int startY, int dx, int dy)

     里面调用的是startScroll(startX, startY, dx, dy, DEFAULT_DURATION) 用我们自己的 DEFAULT_DURATION

(3) 通过反射替换HorizontalScrollView 中的OverScroller对象

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/159409.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • shiro安全框架_漏洞利用及攻击框架

    shiro安全框架_漏洞利用及攻击框架一、Shiro漏洞原理ApacheShiro框架提供了记住我的功能(RemeberMe),用户登录成功后会生成经过加密并编码的cookie。cookie的key为RemeberMe,cookie的值是经过对相关信息进行序列化,然后使用aes加密,最后在使用base64编码处理形成的在服务端接收cookie值时,按以下步骤解析:检索RemeberMecookie的值Base64解码使用ACE解密(加密密钥硬编码)进行反序列化操作(未作过滤处理)在调用反序列化的时候未进行任何过滤,导致

  • 常见算法时间复杂度

    常见算法时间复杂度时间复杂度算法分析同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。一个算法的评价主要从时间复杂度和空间复杂度来考虑。一、时间复杂度(1)时间频度一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费

  • Pytest(1)安装与入门「建议收藏」

    Pytest(1)安装与入门「建议收藏」pytest介绍pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高。根据pytest的官方网站介绍,它

  • Linux nmap命令整理

    Linux nmap命令整理nmap–iflist:查看本地主机的接口信息和路由信息-A:选项用于使用进攻性方式扫描-T4:指定扫描过程使用的时序,总有6个级别(0-5),级别越高,扫描速度越快,但也容易被防火墙或IDS检测并屏蔽掉,在网络通讯状况较好的情况下推荐使用T4-oXtest.xml:将扫描结果生成test.xml文件,如果中断,则结果打不开-oAtest.xml:将扫描结果生成test.xml文件,中断后,结果也可保存-oGtest.txt:将扫描结果生成test…

  • Qt-QCustomplot画静态、动态曲线教程图解

    Qt-QCustomplot画静态、动态曲线教程图解1、QCustomPlot介绍QCustomPlot是一个小型的Qt画图标类,支持绘制静态曲线、动态曲线、多重坐标曲线,柱状图,蜡烛图等。只需要在项目中加入头文件qcustomplot.h和qcustomplot.cpp文件,然后使一个widget提升为QCustomPlot类,即可使用。QCustomPlot官网:http://www.qcustomplot.com/…

    2022年10月17日
  • InstallShield 2010打包

    InstallShield 2010打包打包安装文件。本例子在VS2010下完成,对应的InstallShield2010版的打包程序。如果已安装InstallShield2010,会在VS2010中有体现。新建项目,在已安装的模板中选

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号