1.自定义ViewPager
xml文件
<com.example.viewpager2.MyViewPager
android:layout_width="match_parent"
android:layout_height="450dp"
android:id="@+id/MyViewPager"
/>
实例化
public class MainActivity extends Activity {
private MyViewPager MyViewPager;
int ids[]={R.drawable.a1,R.drawable.a2,R.drawable.a3,R.drawable.a4,R.drawable.a5,R.drawable.a6};
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyViewPager = (MyViewPager) findViewById(R.id.MyViewPager);
//添加页面
for (int i = 0; i < ids.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setBackgroundResource(ids[i]);
//添加到MyViewPager这个View中
MyViewPager.addView(imageView);
}
}
}
指定到对应的图片上
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//遍历孩子,给每个孩子指定在屏幕上的坐标位置
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
childView.layout(i * getWidth(), 0, (i + 1) * getWidth(), getHeight());
}
}
2.手势识别器
* 1.定义出来
private GestureDetector detector;
* 2.实例化-把想要的方法重新
private void initView(final Context context) {
detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent e) {
Toast.makeText(context, "长按", Toast.LENGTH_SHORT).show();
super.onLongPress(e);
}
/**
*
* @param e1
* @param e2
* @param distanceX 在X轴上滑动的距离
* @param distanceY 在轴上滑动的距离
* @return
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Toast.makeText(context, "滚动", Toast.LENGTH_SHORT).show();
/**
* X:要在X轴移动的距离
* Y:要在Y轴移动的距离
* */
scrollBy((int) distanceX, 0);
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
Toast.makeText(context, "双击", Toast.LENGTH_SHORT).show();
return super.onDoubleTap(e);
}
});
}
* 3.在OnTouchEvent()把事件传递给手势识别器
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
//3.把事件传递给手势识别器
detector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
3.scrollBy 正值:左移
负值:右移
scrollTo :起始值+要移动的距离 = 要移动到那里去 ,根据坐标移动
4.回弹 上下页面
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
//3.把事件传递给手势识别器
detector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.w("www","onTouchEvent == ACTION_DOWN");
//1.记录坐标
startX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
Log.w("www","onTouchEvent == ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.w("www","onTouchEvent == ACTION_UP");
//2.来到新的坐标
enddX = event.getX();
//下标位置
int tempIndex = currentIndex;
if ((startX - enddX) > getWidth() / 2) {
//显示下一个页面
tempIndex++;
} else if ((enddX - startX) > getWidth() / 2) {
//显示上一个页面
tempIndex--;
}
//根据下标位置移动到指定页面
scrollToPager(tempIndex);
break;
}
return true;
}
/**
* 屏蔽非法值,根据下标位置移动到指定页面
*
* @param tempIndex
*/
public void scrollToPager(int tempIndex) {
if (tempIndex < 0) {
tempIndex = 0;
} else if (tempIndex > getChildCount() - 1) {
tempIndex = getChildCount() - 1;
}
//当前变量的下标位置
currentIndex = tempIndex;
//移动到指定位置
scrollTo(currentIndex*getWidth(),0);
}
5.解决回弹生硬
a=页面 * getWidth() ,页面*屏幕的宽度
b=getScrollX() ,手指放下的位置,也是回弹的开始位置
c=a-b,页面回弹的总距离
int distanceX = currentIndex * getWidth() - getScrollX();
定义一个 MyScroller 类,传入X轴的起始位置(getScrollX()),Y轴的起始位置(0),在X轴上要移动的距离(distanceX),在Y轴上要移动的距离(0)。
MyScroller scroller = new MyScroller();
scroller.startScroll(getScrollX(), getScrollY(), distanceX, 0);
设置回弹的总时间为300ms,那么 速度=c/300
移动一小段的距离= 移动这一小段所花的时间 *(c/300)
利用系统的时间 SystemClock.uptimeMillis() 来计算 移动这一小段所花的时间。
boolean isFinish;//true移动结束,false:移动完成
computerScrollOffset() ; //true:正在移动 false:移动结束
当移动结束的时候isFinish = true;此时computerScrollOffset()返回false,移动结束。
public class MyScroller {
private float startX;//X轴的起始位置
private float startY;//Y轴的起始位置
private int distanceX;//在X轴上要移动的距离
private int distanceY;//在Y轴上要移动的距离
private long startTime;//开始的时间
private boolean isFinish;//true移动结束
private long totalTime=300;//总时间
public float getCurrX() {
return currX;
}
//得到坐标
public void setCurrX(float currX) {
this.currX = currX;
}
public float currX;
public void startScroll(float startX, float startY, int distanceX, int distanceY){
this.startX=startX;
this.startY=startY;
this.distanceX=distanceX;
this.distanceY=distanceY;
this.startTime= SystemClock.uptimeMillis();//系统开机时间
this.isFinish=false;
}
/**
* 速度
* 求移动一小段的距离
* 求移动一小段对应的坐标
* 求移动一小段对应的时间
* true:正在移动
* false:移动结束
* @return
*/
public boolean computerScrollOffset(){
if(isFinish){
return false;
}
//这一小段结束的时间
long endTime = SystemClock.uptimeMillis();
//移动一小段所花的时间
long passTime = endTime - startTime;
if(passTime < totalTime){
//还没有移动结束
//计算平均速度
// float voleCity = distanceX / totalTime;
//移动这一小段对应的距离
float distanceSmallX = passTime * distanceX / totalTime ;
//移动这一小段后对应的坐标
currX = startX + distanceSmallX; //起始位置+移动的距离
}else{
//移动结束
isFinish = true;
currX = startX + distanceX; //移动结束。起始位置+移动的距离(此时达到最大)
}
return true;
}
}
//刷新
invalidate();
//导致onDraw(),computerScroll()执行
递归调用,直到距离到达总距离
@Override
public void computeScroll() {
super.computeScroll();
if(scroller.computerScrollOffset()){
float currX= scroller.getCurrX();
scrollTo((int)currX,0);
invalidate();
}
}
6.添加RadioGroup 实现页面切换
布局:
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal"
android:id="@+id/radio"
>
</RadioGroup>
实例化:
private RadioGroup radio;
radio = (RadioGroup) findViewById(R.id.radio);
设置radiogroup选中状态的变化:点击哪一个radiobutton就选中对应的页面。
for (int i = 0; i < MyViewPager.getChildCount(); i++) {
RadioButton button = new RadioButton(this);
button.setId(i);
//添加到radiogroup
radio.addView(button);
//默认选择第一个页面
radio.check(0);
//设置radiogroup选中状态的变化
radio.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
/**
* @param group
* @param checkedId 0~5之间
*/
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
//根据下标位置定位到具体每个页面
MyViewPager.scrollToPager(checkedId);
}
});
}
7.设置点击哪一个页面就选中对应的radiobutton----利用接口回传。
首先学习一下点击事件的接口和调用。
7.1.定义接口
public interface OnClickListener{
Void onClick(View v);
}
//设置监听页面的改变
public interface OnPagerChangListener{
/**
* 当页面改变的时候回调这个方法
* @param position 当前页面的下标
* @return
*/
void onScrollToPager(int position);
}
让使用者传递接口的实例进来
public void setOnClickListener(@Nullable OnClickListener l){
If(!isClickable()){
setClickable(true);
}
setListenerInfo().mOnClickListener = l;
}
private OnPagerChangListener mOnPagerChangListener;
/**
* 设置页面改变的监听
* @param l
*/
public void setOnPagerChangListener(OnPagerChangListener l){
mOnPagerChangListener = l;
}
7.2.调用方法
li.mOnCLickListener.onClick(this);
if(mOnPagerChangListener != null){
mOnPagerChangListener.onScrollToPager(currentIndex);
}
哪个地方有变化就在哪里写接口
视图有变化,在视图里面写接口
7.3.用户使用
View.setOnClickListener(new View.OnClickListener(){
@Override
Public void onClick(View v){
}
});
7.4.回调
view.setOnClickListener(new View.OnClickListener(){
@Override
Public void onClick(View v){
//点击事件就被执行
}
});
//设置监听页面的改变
MyViewPager.setOnPagerChangListener(new MyViewPager.OnPagerChangListener(){
/**
* 0~5
* @param position 当前页面的下标
* @return
*/
@Override
public void onScrollToPager(int position) {
radio.check(position);
}
8.添加一个页面
test.xml 上面是三个进度条,下面是一个scrollview,里面包含很多个textview。
//添加test.xml页面
View testView = View.inflate(this,R.layout.test,null);
MyViewPager.addView(testView,2);
记得onMeadure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for(int i = 0;i<getChildCount();i++){
View child = getChildAt(i);
child.measure(widthMeasureSpec,heightMeasureSpec);
}
}
9.事件冲突
scrollview 和 viewpager
问题:可以滑动scrollview,不能滑动viewpager。(上下滑动 和 左右滑动的冲突)
scrollview 把事件处理了
解决:
onInterceptTouchEvent()拦截事件:如果当前方法返回true,拦截事件,将会触发当前控件的onTouchEvent,如果当前返回false,不拦截事件,事件继续分发。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
super.onInterceptTouchEvent(ev);
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
问题:可以滑动viewpager,不能滑动scrollview。
解决:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
super.onInterceptTouchEvent(ev);
detector.onTouchEvent(ev);
boolean result = false;//默认传递给孩子
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.w("www","onInterceptTouchEvent == ACTION_DOWN");
//1.记录坐标
downX = ev.getX();
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.w("www","onInterceptTouchEvent == ACTION_MOVE");
//2.记录结束值
float endX = ev.getX();
float endY = ev.getY();
//3.计算绝对值
float distanceX = Math.abs(endX - downX);
float distanceY = Math.abs(endY - downY);
if(distanceX > distanceY && distanceX > 5 ){
result = true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return result;
}
解决完:滑动会闪
把onInterceptTouchEvent事件传给手势识别器
10.事件传递机制
11.关于scrollTo,scrollBy,startX,startY,getScrollX(),getScrollY()
getScrollX=0,childView. getScrollX()会随着移动改变它的值。
源码:
MyViewPager
public class MyViewPager extends ViewGroup {
private MyScroller scroller;
/**
* 手势识别器
* 1.定义出来
* 2.实例化-把想要的方法重新
* 3.在OnTouchEvent()把事件传递给手势识别器
*
* @param context
* @param attrs
*/
public MyViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private GestureDetector detector;
//2.实例化
private void initView(final Context context) {
scroller = new MyScroller();
detector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent e) {
Toast.makeText(context, "长按", Toast.LENGTH_SHORT).show();
super.onLongPress(e);
}
/**
*
* @param e1
* @param e2
* @param distanceX 在X轴上滑动的距离
* @param distanceY 在轴上滑动的距离
* @return
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
Toast.makeText(context, "滚动", Toast.LENGTH_SHORT).show();
/**
* X:要在X轴移动的距离
* Y:要在Y轴移动的距离
* */
scrollBy((int) distanceX, 0);
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
Toast.makeText(context, "双击", Toast.LENGTH_SHORT).show();
return super.onDoubleTap(e);
}
});
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//遍历孩子,给每个孩子指定在屏幕上的坐标位置
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
childView.layout(i * getWidth(), 0, (i + 1) * getWidth(), getHeight());
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
for(int i = 0;i<getChildCount();i++){
View child = getChildAt(i);
child.measure(widthMeasureSpec,heightMeasureSpec);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
private float startX;
private float enddX;
private int currentIndex;
private float downX;
private float downY;
/**
* 如果当前方法返回true,拦截事件,将会触发当前控件的onTouchEvent
* 如果当前返回false,不拦截事件,事件继续分发
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
super.onInterceptTouchEvent(ev);
detector.onTouchEvent(ev);
boolean result = false;//默认传递给孩子
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.w("www","onInterceptTouchEvent == ACTION_DOWN");
//1.记录坐标
downX = ev.getX();
downY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.w("www","onInterceptTouchEvent == ACTION_MOVE");
//2.记录结束值
float endX = ev.getX();
float endY = ev.getY();
//3.计算绝对值
float distanceX = Math.abs(endX - downX);
float distanceY = Math.abs(endY - downY);
if(distanceX > distanceY && distanceX > 5 ){
result = true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return result;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
//3.把事件传递给手势识别器
detector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.w("www","onTouchEvent == ACTION_DOWN");
//1.记录坐标
startX = event.getX();
break;
case MotionEvent.ACTION_MOVE:
Log.w("www","onTouchEvent == ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.w("www","onTouchEvent == ACTION_UP");
//2.来到新的坐标
enddX = event.getX();
//下标位置
int tempIndex = currentIndex;
if ((startX - enddX) > getWidth() / 2) {
//显示下一个页面
tempIndex++;
} else if ((enddX - startX) > getWidth() / 2) {
//显示上一个页面
tempIndex--;
}
//根据下标位置移动到指定页面
scrollToPager(tempIndex);
break;
}
return true;
}
/**
* 屏蔽非法值,根据下标位置移动到指定页面
*
* @param tempIndex
*/
public void scrollToPager(int tempIndex) {
if (tempIndex < 0) {
tempIndex = 0;
} else if (tempIndex > getChildCount() - 1) {
tempIndex = getChildCount() - 1;
}
//当前变量的下标位置
currentIndex = tempIndex;
if(mOnPagerChangListener != null){
mOnPagerChangListener.onScrollToPager(currentIndex);
}
//移动到指定位置
// scrollTo(currentIndex*getWidth(),0);
// scroller = new MyScroller();
//总距离
int distanceX = currentIndex * getWidth() - getScrollX();
scroller.startScroll(getScrollX(), getScrollY(), distanceX, 0);
//刷新
invalidate();//导致onDraw(),computerScroll()执行
}
@Override
public void computeScroll() {
super.computeScroll();
if(scroller.computerScrollOffset()){
float currX= scroller.getCurrX();
scrollTo((int)currX,0);
invalidate();
}
}
//1.定义接口
//设置监听页面的改变
public interface OnPagerChangListener{
/**
* 当页面改变的时候回调这个方法
* @param position 当前页面的下标
* @return
*/
void onScrollToPager(int position);
}
private OnPagerChangListener mOnPagerChangListener;
/**
* 设置页面改变的监听
* @param l
*/
public void setOnPagerChangListener(OnPagerChangListener l){
mOnPagerChangListener = l;
}
}
MyScroller
public class MyScroller {
private float startX;//X轴的起始位置
private float startY;//Y轴的起始位置
private int distanceX;//在X轴上要移动的距离
private int distanceY;//在Y轴上要移动的距离
private long startTime;//开始的时间
private boolean isFinish;//true移动结束
private long totalTime=300;//总时间
public float getCurrX() {
return currX;
}
//得到坐标
public void setCurrX(float currX) {
this.currX = currX;
}
public float currX;
public void startScroll(float startX, float startY, int distanceX, int distanceY){
this.startX=startX;
this.startY=startY;
this.distanceX=distanceX;
this.distanceY=distanceY;
this.startTime= SystemClock.uptimeMillis();//系统开机时间
this.isFinish=false;
}
/**
* 速度
* 求移动一小段的距离
* 求移动一小段对应的坐标
* 求移动一小段对应的时间
* true:正在移动
* false:移动结束
* @return
*/
public boolean computerScrollOffset(){
if(isFinish){
return false;
}
//这一小段结束的时间
long endTime = SystemClock.uptimeMillis();
//移动一小段所花的时间
long passTime = endTime - startTime;
if(passTime < totalTime){
//还没有移动结束
//计算平均速度
// float voleCity = distanceX / totalTime;
//移动这一小段对应的距离
float distanceSmallX = passTime * distanceX / totalTime ;
//移动这一小段后对应的坐标
currX = startX + distanceSmallX; //起始位置+移动的距离
}else{
//移动结束
isFinish = true;
currX = startX + distanceX; //移动结束。起始位置+移动的距离(此时达到最大)
}
return true;
}
}
MainActivity
public class MainActivity extends Activity {
private MyViewPager MyViewPager;
int ids[]={R.drawable.a1,R.drawable.a2,R.drawable.a3,R.drawable.a4,R.drawable.a5,R.drawable.a6};
private RadioGroup radio;
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyViewPager = (MyViewPager) findViewById(R.id.MyViewPager);
radio = (RadioGroup) findViewById(R.id.radio);
//添加页面
for (int i = 0; i < ids.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setBackgroundResource(ids[i]);
//添加到MyViewPager这个View中
MyViewPager.addView(imageView);
}
//添加test.xml页面
View testView = View.inflate(this,R.layout.test,null);
MyViewPager.addView(testView,2);
for (int i = 0; i < MyViewPager.getChildCount(); i++) {
RadioButton button = new RadioButton(this);
button.setId(i);
//添加到radiogroup
radio.addView(button);
//默认选择第一个页面
radio.check(0);
//设置radiogroup选中状态的变化
radio.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
/**
* @param group
* @param checkedId 0~5之间
*/
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
//根据下标位置定位到具体每个页面
MyViewPager.scrollToPager(checkedId);
}
});
//设置监听页面的改变
MyViewPager.setOnPagerChangListener(new MyViewPager.OnPagerChangListener() {
/**
* 0~5
*
* @param position 当前页面的下标
* @return
*/
@Override
public void onScrollToPager(int position) {
radio.check(position);
}
});
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context="com.example.viewpager2.MainActivity"
android:orientation="vertical"
>
<com.example.viewpager2.MyViewPager
android:layout_width="match_parent"
android:layout_height="450dp"
android:id="@+id/MyViewPager"
android:layout_below="@id/radio"
/>
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal"
android:id="@+id/radio"
>
</RadioGroup>
</LinearLayout>