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对象

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • jenkins自动触发构建_秒级定时触发器

    jenkins自动触发构建_秒级定时触发器前言跑自动化用例每次用手工点击jenkins出发自动化用例太麻烦了,我们希望能每天固定时间跑,这样就不用管了,坐等收测试报告结果就行。jenkins的定时任务是用的crontab语法定时构建语法

    2022年7月29日
    10
  • phpstorm2020激活码【2021最新】[通俗易懂]

    (phpstorm2020激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html00OE5RWT28-eyJsa…

    2022年3月30日
    679
  • jenkins 邮件_邮件发送服务

    jenkins 邮件_邮件发送服务前言前面已经实现在jenkins上展示html的测试报告,接下来只差最后一步,把报告发给你的领导,展示你的劳动成果了。安装EmailExtensionPlugin插件jenkins首页-

    2022年7月29日
    4
  • Arthas – Java 线上问题定位处理的终极利器「建议收藏」

    Arthas – Java 线上问题定位处理的终极利器「建议收藏」前言在使用Arthas之前,当遇到Java线上问题时,如CPU飙升、负载突高、内存溢出等问题,你需要查命令,查网络,然后jps、jstack、jmap、jhat、jstat、hprof等一通操作。最终焦头烂额,还不一定能查出问题所在。而现在,大多数的常见问题你都可以使用Arthas轻松定位,迅速解决,及时止损,准时下班。1、Arthas介绍Arthas是Alib…

    2022年10月22日
    0
  • 谈谈Google AdSense以外的国外优秀广告联盟

    谈谈Google AdSense以外的国外优秀广告联盟无论国内其他的广告联盟吹捧自己有多好,其实我们站长的心理早就有数了,国内最好的广告联盟也就是3强争霸,征战不休!不知道大家了解不了解!其实做国外有些联盟简直就是暴利,赚钱比AdSense快多了,只不过很多人都不知道罢了,今天我写这篇文章就是为了让大家了解到更多的联盟!这绝对是本人原创,费尽了时间和精力才写成的,所以请各位认真的看,谢谢!GoogleAdSense、百度推广、阿里妈妈各有各的优点,GoogleAdSense的有点最近越来越不明显了,但是有点可以肯定,那就是技术最高、广告与网站内容最为匹

    2022年9月19日
    0
  • 视频文件无效怎么修复_google chrome怎么设置兼容模式

    视频文件无效怎么修复_google chrome怎么设置兼容模式发现问题以Ubuntu系统为例,我们通过genpac生成autoproxy.pac文件,然后点击系统设置->网络->代理设置->自动,在输入框中输入file://绝对路径/autoproxy.pac。设置好以后,Chrome应当可以自动切换网络,但是Chrome无法访问google的搜索引擎,而火狐浏览器可以正常访问。分析问题出现上面问题的唯一可能就是Chrome设置有误,…

    2022年10月18日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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