热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用Scroller完成一个简单的ViewPager

前言ViewPager是我们常用的控件之一,此篇文章我们用Scroller等知识实现一个简单的ViewPager。效果:(源码在文章结尾)涉及知识点onMeasure和onLayout

前言

ViewPager是我们常用的控件之一,此篇文章我们用Scroller等知识实现一个简单的ViewPager。

效果:

(源码在文章结尾)

这里写图片描述

涉及知识点

onMeasure和onLayout

此点若不了解可以参考郭霖前辈的文章:
Android视图绘制流程完全解析,带你一步步深入了解View(二)

事件分发机制

此点可以参考笔者文章:
完全理解android事件分发机制

scrollTo和scrollBy

scrollTo:以View的初始位置为起点进行移动
scrollBy:以View的当前位置为起点进行移动

TouchSlop

系统可以识别出的被认为是滑动的最小距离。如果大于这个距离则是滑动。

View.getScrollX()

getScrollX()获取到的值是屏幕的最左侧在整个空间中所占位置的X值。
打个比方:View是一条6米的绳子,而屏幕只能看到2~4米的绳子。那么getScrollX()的值就为2。如果屏幕看到的是3~5米的绳子,那么getScrollX()的值就为3。

Scroller

Scroller的使用主要为3步:
1、初始化Scroller
2、重写computeScroll()方法
computeScroll()是在View的draw的时候调用的,而invalidate会导致View重绘,所以在重写computeScroll()之后,我们要使用invalidate()来间接调用它。
过程为:invalidate()->draw()->computeScroll()
3、使用startScroll()开启滑动

代码:

MainActivity:

package com.example.double2.scrollertest;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private Button btnOne;
private Button btnTwo;
private Button btnThree;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btnOne=(Button)findViewById(R.id.btn_one);
btnTwo=(Button)findViewById(R.id.btn_two);
btnThree=(Button)findViewById(R.id.btn_three);
btnOne.setOnClickListener(this);
btnTwo.setOnClickListener(this);
btnThree.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_one:
Toast.makeText(this, "One", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_two:
Toast.makeText(this, "Two", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_three:
Toast.makeText(this, "Three", Toast.LENGTH_SHORT).show();
break;
}
}
}

ScrollerLayout:

package com.example.double2.scrollertest;

import android.content.Context;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
* 项目名称:ScrollerTest
* 创建人:Double2号
* 创建时间:2017.2.23 12:02
* 修改备注:
*/

public class ScrollerLayout extends ViewGroup {

private Scroller mScroller;
//用于判断是否是滑动
private int mTouchSlop;
//按下时的X坐标
private float downX;
//滑动时的X坐标
private float moveX;
//上次触发ACTION_MOVE事件时的屏幕坐标
private float lastMoveX;
//界面可滚动的左边界
private int leftBorder;
//界面可滚动的右边界
private int rightBorder;


public ScrollerLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// 创建Scroller的实例
mScroller = new Scroller(context);
ViewConfiguration cOnfiguration= ViewConfiguration.get(context);
// 获取TouchSlop值
mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);

}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i View childView = getChildAt(i);
// 为ScrollerLayout中的每一个子控件测量大小
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed) {
int childCount = getChildCount();
for (int i = 0; i View childView = getChildAt(i);
// 为ScrollerLayout中的每一个子控件在水平方向上进行布局
childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
}
// 初始化左右边界值
leftBorder = getChildAt(0).getLeft();
rightBorder = getChildAt(getChildCount() - 1).getRight();
}
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = ev.getRawX();
lastMoveX = downX;
break;
case MotionEvent.ACTION_MOVE:
moveX = ev.getRawX();
float diff = Math.abs(moveX - downX);
lastMoveX = moveX;
// 当手指拖动值大于TouchSlop值时,认为应该进行滑动,不进行点击事件
if (diff > mTouchSlop) {
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
moveX = event.getRawX();
int scrolledX = (int) (lastMoveX - moveX);
if (getScrollX() + scrolledX scrollTo(leftBorder, 0);
return true;
} else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
scrollTo(rightBorder - getWidth(), 0);
return true;
}
scrollBy(scrolledX, 0);
lastMoveX = moveX;
break;
case MotionEvent.ACTION_UP:
// 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面
//如果当前X超过一半的宽度,那么就向左移
//如果当前的X小鱼一半的宽度就向右移
int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
int dx = targetIndex * getWidth() - getScrollX();

mScroller.startScroll(getScrollX(), 0, dx, 0);// 调用startScroll()方法来初始化滚动数据并刷新界面
invalidate();//通过invalidate()间接调用computeScroll()

break;
}
return super.onTouchEvent(event);
}

@Override
public void computeScroll() {
// computeScroll()是不会自己调用,只能通过invalidate()->draw()->computeScroll()来间接调用
//computeScrollOffset()用来判断是否完成了整个华东
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
}

activity_main:


<com.example.double2.scrollertest.ScrollerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

<Button
android:id="@+id/btn_one"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="One"/>


<Button
android:id="@+id/btn_two"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="Two"/>


<Button
android:id="@+id/btn_three"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="Three"/>

com.example.double2.scrollertest.ScrollerLayout>

源码地址:

http://download.csdn.net/detail/double2hao/9762018


推荐阅读
  •  项目地址https:github.comffmydreamWiCar界面做的很难看,美工方面实在不在行。重点是按钮触摸事件的处理,这里搬了RepeatListener项目代码,例 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • php缓存ri,浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
    thinkPHP的F方法只能用于缓存简单数据类型,不支持有效期和缓存对象。S()缓存方法支持有效期,又称动态缓存方法。本文是小编日常整理有关thinkp ... [详细]
  • android 触屏处理流程,android触摸事件处理流程 ? FOOKWOOD「建议收藏」
    android触屏处理流程,android触摸事件处理流程?FOOKWOOD「建议收藏」最近在工作中,经常需要处理触摸事件,但是有时候会出现一些奇怪的bug,比如有时候会检测不到A ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 标题: ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
  • 本文介绍了pack布局管理器在Perl/Tk中的使用方法及注意事项。通过调用pack()方法,可以控制部件在显示窗口中的位置和大小。同时,本文还提到了在使用pack布局管理器时,应注意将部件分组以便在水平和垂直方向上进行堆放。此外,还介绍了使用Frame部件或Toplevel部件来组织部件在窗口内的方法。最后,本文强调了在使用pack布局管理器时,应避免在中间切换到grid布局管理器,以免造成混乱。 ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
author-avatar
唐耿铠1_747
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有