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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python+pycharm+selenium+谷歌浏览器驱动 自动化环境部署

    python+pycharm+selenium+谷歌浏览器驱动 自动化环境部署准备工作:  第一步:安装python。打开网址https://www.python.org/downloads/windows/现在最新版本3.7,本人使用的是3.6。  第二步:安装pycharm。打开网址:https://www.jetbrains.com/pycharm/download/#section=windows有开源版和试用版,开源即免费,试用版试用期过后要$$。安装试用版过程中它会弹出个注册界面,关于破解自行搜索。开源版则没有…

    2022年5月12日
    52
  • 让Firefox支持ActiveX控件「建议收藏」

    让Firefox支持ActiveX控件「建议收藏」让Firefox支持ActiveX控件  疑难集锦字号  我现在用Firefox作为主力浏览器,Firefox下能否支持ActiveX插件?所为对某些包含ActiveX控件的网站并不兼容,不知道怎么做能让Firefox支持ActiveX呢?      答:两种方案:一种是使用IETab这个插件,在Firefox下直接调用IE浏览器内核进行浏览;      另一种是使

    2022年5月14日
    329
  • linux网络随机ip发包工具,三款常用IP发包工具介绍

    linux网络随机ip发包工具,三款常用IP发包工具介绍1.简介在从事网络产品尤其是网络安全产品开发时,我们一直面临着一个问题,就是对产品的TCP/IP协议栈进行稳定性或安全性测试,确保开发产品在遇到各种不规则的错误的IP包时仍可正常稳定高效地工作,我们知道,在正常的网络环境中,很难产生错误的IP包,也很难产生我们想要的错误的IP包,为此,要完成对产品的测试,我们必须自己来制造各种各样错误的IP包,本篇的目的就是介绍如何利用各种发包工具来制…

    2022年9月6日
    4
  • Pytest(11)allure报告「建议收藏」

    Pytest(11)allure报告「建议收藏」前言allure是一个report框架,支持java的Junit/testng等框架,当然也可以支持python的pytest框架,也可以集成到Jenkins上展示高大上的报告界面。mac环境:

    2022年7月29日
    7
  • pycharm崩溃_pubg lite手机闪退报错

    pycharm崩溃_pubg lite手机闪退报错问题描述今天在用pycharm跑模型时,突然报错自OutofMemoryThereisnotenoughmemorytoperformtherequestedoperation.Pleaseincrease‘Xmx’settingandrestarttheIDEforchangetotakeeffect.解决方案找到pycharm安装目录bin下的pycharm64.exe.vmoptions,用记事本打开将前两个适当加大找到bin目录下的P

    2022年8月26日
    5
  • Java-Object转JSONObject

    Java-Object转JSONObject第一种方式importcom.alibaba.fastjson.JSONArray;importcom.alibaba.fastjson.JSONObject;Objectimages=datum.getImages();if(null!=images){StringdoImages=(String)datum.getImages();charc=doImages.charAt(0);charb=doImages.charAt(d

    2022年4月29日
    76

发表回复

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

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