ViewDragHelper
https://developer.android.com/reference/android/support/v4/widget/ViewDragHelper?hl=en
scrollBy与scrollTo的区别
scrollTo(x,y) 表示移动到一个具体的坐标点(x.y)
而scrollBy(dx,dy)表示移动的增量 为dx dy
获取偏移量后使用scrollBy来移动View,如下
int offsetX = x-lastX;
int offsetY = y - lastY;
scrollTo scrollBy 方法移动的是View的content 即让View 的内容移动
如果在ViewGroup 中使用scrollTo scrollBy 方法,那么移动的将是所有子View
如果在View 中使用,那么移动的将是View的内容
例如 TextView content就是他的文本 ImageView content 就是它的drawable对象
如果要移动一个View可以通过它的父类,调用scrollBy方法 Parent.scrollBy(-dx,-dy) 增量要变成负值
package com.yifei.myapplication;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
public class MyScrollByView extends View {
private int lastX;
private int lastY;
public MyScrollByView(Context context) {
super(context);
initView();
}
public MyScrollByView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MyScrollByView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyScrollByView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView();
}
private void initView() {
setBackgroundColor(Color.YELLOW);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取触摸点的坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN://记录触摸点的坐标
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE://计算偏移量
int offsetX = x - lastX;
int offsetY = y - lastY;
ViewGroup parent = (ViewGroup) getParent();
parent.scrollBy(-offsetX,-offsetY);
break;
case MotionEvent.ACTION_UP:
break;
default:
return false;
}
return true;
}
}
六
Scroller
Scroller 原理实现
它与前面使用的scrollTo 和scrollBy 方法来实现自view 跟随手指移动的原理基本类似
虽然scrollBy方法是让子View瞬间从某点移动到另一个点,但是由于在ACTION_MOVE事件中
不断获取手指移动的微小的偏移量,这样就将一段距离分成了N个非常小的偏移量,这个原理与动画的实现原理也是基本
类似的,他们都是利用了人眼的视觉暂留特性
七 属性动画
实现一个滑动的效果
package com.yifei.myapplication;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Scroller;
public class MyScrollerView extends View {
private int lastX;
private int lastY;
private Scroller mScroller;
public MyScrollerView(Context context) {
super(context);
initView(context);
}
public MyScrollerView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
}
private void initView(Context context) {
setBackgroundColor(Color.BLUE);
//初始化Scroller
mScroller = new Scroller(context);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getX();
lastY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - lastX;
int offsetY = y - lastY;
((ViewGroup) getParent()).scrollBy(-offsetX, -offsetY);
break;
case MotionEvent.ACTION_UP://手指抬起操作
//使用startScroll() 方法开启模拟过程
// mScroller.startScroll();
View viewGroup = (View) getParent();
Log.d("MyScrollerView", "getScrollX--"+getScrollX()+"--getScrollY--"+getScrollY());
mScroller.startScroll(
viewGroup.getScrollX(),
viewGroup.getScrollY(),
-viewGroup.getScrollX(),
-viewGroup.getScrollY());
invalidate();//重绘
break;
default:
return false;
}
return true;
}
//重写computeScroll() 实现重写模拟滑动
@Override
public void computeScroll() { //计算滚动的距离
super.computeScroll();
if (mScroller.computeScrollOffset()) {//如果scroll是在滑动 是否已经完成滑动
//scrollTo(x,y) 表示具体移动到一个具体点的坐标点 (x,y)
//scrollBy(dx,dy) 表示移动的增量,移动了多少
//getCurrX(),getCurrY()通过这两个方法获得当前的滑动坐标
((View) getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //获得当前点的坐标
//通过重绘制,不断的调用computeScroll这个方法
//执行流程,invalidate ()--->onDraw()----->invalidate()
invalidate();
}
}
}
八 ViewDragHelper
理解ViewDragHelper
测量并且将View摆放按上去
package com.yifei.myapplication;
import android.content.Context;
import android.icu.util.Measure;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class MyDrawViewGroup extends ViewGroup {
View redView;
View blueView;
public MyDrawViewGroup(Context context) {
super(context);
}
public MyDrawViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
//初始化子View的引用 当ViewGroup 的xml结束标签被读取完成,会执行该方法
//此时会知道自己有几个子控件
//onFinishInflate() 方法一般用来初始化子控件
@Override
protected void onFinishInflate() {
super.onFinishInflate();
redView = getChildAt(0);
blueView = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measuerSize = MeasureSpec.makeMeasureSpec(redView.getLayoutParams().width, MeasureSpec.EXACTLY);//测量模式,为精准模式
redView.measure(measuerSize,measuerSize);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
redView.layout(0,0,redView.getMeasuredWidth(),redView.getMeasuredHeight());//把红色的View摆放上去
blueView.layout(0,redView.getMeasuredHeight(),redView.getMeasuredWidth(),redView.getHeight()*2);
}
}
把View 放在中间
package com.yifei.myapplication;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class MyDrawViewGroup extends ViewGroup {
View redView;
View blueView;
public MyDrawViewGroup(Context context) {
super(context);
}
public MyDrawViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
//初始化子View的引用 当ViewGroup 的xml结束标签被读取完成,会执行该方法
//此时会知道自己有几个子控件
//onFinishInflate() 方法一般用来初始化子控件
@Override
protected void onFinishInflate() {
super.onFinishInflate();
redView = getChildAt(0);
blueView = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measuerSize = MeasureSpec.makeMeasureSpec(redView.getLayoutParams().width, MeasureSpec.EXACTLY);//测量模式,为精准模式
redView.measure(measuerSize, measuerSize);
blueView.measure(measuerSize, measuerSize);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left =getWidth()/2-redView.getMeasuredWidth()/2;
int top =0;
redView.layout(left, top, left+redView.getMeasuredWidth(), redView.getMeasuredHeight());//把红色的View摆放上去
blueView.layout(left, redView.getBottom(), left+redView.getMeasuredWidth(), redView.getBottom() +redView.getHeight());
}
}
加上padding
package com.yifei.myapplication;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class MyDrawViewGroup extends ViewGroup {
View redView;
View blueView;
public MyDrawViewGroup(Context context) {
super(context);
}
public MyDrawViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
//初始化子View的引用 当ViewGroup 的xml结束标签被读取完成,会执行该方法
//此时会知道自己有几个子控件
//onFinishInflate() 方法一般用来初始化子控件
@Override
protected void onFinishInflate() {
super.onFinishInflate();
redView = getChildAt(0);
blueView = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measuerSize = MeasureSpec.makeMeasureSpec(redView.getLayoutParams().width, MeasureSpec.EXACTLY);//测量模式,为精准模式
redView.measure(measuerSize, measuerSize);
blueView.measure(measuerSize, measuerSize);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left =getPaddingLeft()+getWidth()/2-redView.getMeasuredWidth()/2;//把View放在中间 计算padding
int top =0+redView.getPaddingTop();//加上padding
redView.layout(left, top, left+redView.getMeasuredWidth(),top+ redView.getMeasuredHeight());//把红色的View摆放上去
blueView.layout(left, redView.getBottom(), left+redView.getMeasuredWidth(), redView.getBottom() +redView.getHeight());
}
}
另外一种测量方式
package com.yifei.myapplication;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
public class MyDrawViewGroup extends ViewGroup {
View redView;
View blueView;
public MyDrawViewGroup(Context context) {
super(context);
}
public MyDrawViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MyDrawViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
//初始化子View的引用 当ViewGroup 的xml结束标签被读取完成,会执行该方法
//此时会知道自己有几个子控件
//onFinishInflate() 方法一般用来初始化子控件
@Override
protected void onFinishInflate() {
super.onFinishInflate();
redView = getChildAt(0);
blueView = getChildAt(1);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//没有特殊要求 可以这样做
measureChild(redView,widthMeasureSpec,heightMeasureSpec);
measureChild(blueView,widthMeasureSpec,heightMeasureSpec); //另外一种测量方式
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left =getPaddingLeft()+getWidth()/2-redView.getMeasuredWidth()/2;
int top =0+redView.getPaddingTop();
redView.layout(left, top, left+redView.getMeasuredWidth(),top+ redView.getMeasuredHeight());//把红色的View摆放上去
blueView.layout(left, redView.getBottom(), left+redView.getMeasuredWidth(), redView.getBottom() +redView.getHeight());
}
}