Android--自定义View滑动的六种方法
来源:互联网 发布:电脑配件京东还是淘宝 编辑:程序博客网 时间:2024/04/30 03:05
概述
由于移动平台屏幕尺寸大小的限制,为了给用户呈现更好的页面内容,我们需要通过滑动来实现一些页面内容的显示和隐藏操作。
自定义View的方法
- View.layout()方法
- 改变view的布局参数LayoutParams
- View.offsetLeftAndRight()和View.offsetTopAndBottom()方法
- 属性动画ObjectAnimator.ofFloat()方法
- 使用View动画
- View.scrollto()和View.scrollby()方法
- 各种方法的比较
例子
实现自定义view跟随屏幕的点击和拖动自动滑动:代码如下:
自定义view:滑动方法在redirectViewPosition中
package com.example.myscroller;import android.content.Context;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;/** * Created by pingkun.huang on 2016/4/21. */public class MyView1 extends View{ public MyView1(Context context) { super(context); } public MyView1(Context context, AttributeSet attrs) { super(context, attrs); } public MyView1(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void redirectViewPosition(float clickXPosition, float clickYPosition) { int offsetX = (int) (clickXPosition - getLeft()); int offsetY = (int) (clickYPosition - getTop()); //-------第一种方法 调用View的layout()方法 layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY); //-------第二种方法 改变View的布局参数 ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams(); marginLayoutParams.leftMargin = getLeft() + offsetX; marginLayoutParams.topMargin = getTop() + offsetY; //可以用requestLayout()或者setLayoutParams setLayoutParams(marginLayoutParams); //-------第三种方法 offsetLeftAndRight(offsetX); offsetTopAndBottom(offsetY); }}
Activity:
package com.example.myscroller;import android.animation.ObjectAnimator;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.MotionEvent;import android.view.View;import android.view.animation.Animation;import android.view.animation.AnimationUtils;import android.widget.Scroller;public class MainActivity extends AppCompatActivity { MyView1 myView1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myView1 = (MyView1) this.findViewById(R.id.hpk); } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: myView1.redirectViewPosition(event.getX(), event.getY()); case MotionEvent.ACTION_MOVE: myView1.redirectViewPosition(event.getX(),event.getY()); break; } return super.onTouchEvent(event); }}
布局文件:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.myscroller.MainActivity"> <com.example.myscroller.MyView1 android:id="@+id/hpk" android:background="#00ff00" android:layout_width="100dp" android:layout_height="100dp" > </com.example.myscroller.MyView1></RelativeLayout>
1:View.layout()
view在被绘制到屏幕上的时候会先调用onMeasure()方法来确定view的大小,之后会调用onLayout()方法来告诉父容器view的具体位置,view位置的确定有四个参数来决定left、top、right、bottom,我们可以通过改变这四个参数来实现View的滑动。layout()方法的声明为:
public void layout(int left, int top, int right, int bottom)
left:view的左边距和父容器的左边距的差
top:view的上边距和父容器的上边距的差
right:view的右边距和父容器的左边距的差
bottom:veiw的下边距和父容器的上边距的差
view的这四个参数都是相对它的父容器而言的。原始值可通过getLeft(),getTop(),getRight(),getBottom()来获取,且通过这四个函数获取的值是固定的,拖放过程中并不会改变着四个函数的返回值。
public void redirectViewPosition(float clickXPosition, float clickYPosition) { int offsetX = (int) (clickXPosition - getLeft()); int offsetY = (int) (clickYPosition - getTop()); //第一种方法 调用View的layout()方法 layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY);
clickXposition:当前屏幕点击的x坐标可通过MotionEvent.getX()来取得。
clickYposition:当前屏幕点击的y坐标可通过MotionEvetnt.getY()来取得
offsetX:当前点击位置的x坐标和getLeft()返回值的差值,就是滑动后x轴偏移量
offsetY:当前点击位置的y坐标和getTop()返回值的差值,就是滑动后y轴的偏移量。
最后计算view当前位置相对于父容器的left,top,right,bottom值。在屏幕滑动过程中会不断的调用layout方法,重新绘制view。
注意:
getX/getY返回的是相对于当前View父容器左上角的x坐标和y坐标,而getRawX/getRawY 返回的是相对于手机屏幕左上角的x和y坐标
2:改变view的布局参数LayoutParams
LayoutParams保存了View的位置参数,因此我们可以通过改变LayoutParams的属性值来打到滑动View的目的。
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams(); marginLayoutParams.leftMargin = getLeft() + offsetX; marginLayoutParams.topMargin = getTop() + offsetY; //可以用requestLayout()或者setLayoutParams setLayoutParams(marginLayoutParams);
MarginLayoutParams类 继承自ViewGroup.LayoutParams,有leftMargin,topMargin,rightMargin,bottomMargin等变量。因此我们可以修改这些margin值,之后调用setLayoutParams或requestLayout来使这些修改后的变量生效。
3:View.offsetLeftAndRight()和View.offsetTopAndBottom()方法
offsetLeftAndRight(offsetX);offsetTopAndBottom(offsetY);
这两个方法分别设定了view的水平和垂直偏移量,其实在函数内部利用偏移量计算了view当前相对于父布局的上,下,左,右边距。内部中mLeft += offset; mRight += offset; mTop += offset; mBottom += offset;
public void offsetLeftAndRight(int offset) { if (offset != 0) { final boolean matrixIsIdentity = hasIdentityMatrix(); if (matrixIsIdentity) { if (isHardwareAccelerated()) { invalidateViewProperty(false, false); } else { final ViewParent p = mParent; if (p != null && mAttachInfo != null) { final Rect r = mAttachInfo.mTmpInvalRect; int minLeft; int maxRight; if (offset < 0) { minLeft = mLeft + offset; maxRight = mRight; } else { minLeft = mLeft; maxRight = mRight + offset; } r.set(0, 0, maxRight - minLeft, mBottom - mTop); p.invalidateChild(this, r); } } } else { invalidateViewProperty(false, false); } mLeft += offset; mRight += offset; mRenderNode.offsetLeftAndRight(offset); if (isHardwareAccelerated()) { invalidateViewProperty(false, false); invalidateParentIfNeededAndWasQuickRejected(); } else { if (!matrixIsIdentity) { invalidateViewProperty(false, true); } invalidateParentIfNeeded(); } notifySubtreeAccessibilityStateChangedIfNeeded(); } }
属性动画ObjectAnimator.ofFloat()方法
ObjectAnimator.ofFloat(myView1,"translationX",0,100).setDuration(100).start();
函数签名为:
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) { ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setFloatValues(values); return anim; }
target:属性动画所作用的对象
propertyName:target对象的动画属性,必须在target内部有相应的set方法。你要设置动画的对象的属性必须有一个set该值的方法。因为ObjectAnimator在动画的过程中自动更新属性值,这是通过调用该属性的set方法来实现的
values:动画属性的起始值和结束值。
可以参考:这篇文章
使用View动画
使用动画主要是改变View的translationX和translationY属性
Animation animation = AnimationUtils.loadAnimation(this, R.anim.weilin); myView1.startAnimation(animation);
loadAnimation来加载xml格式的动画文件,之后调用startAnimation来启动动画。
res目录下新建一个anim文件夹,创建一个动画资源,如下:
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true" android:duration="100" android:shareInterpolator="@android:anim/accelerate_interpolator"> <translate android:duration="100" android:fromXDelta="0" android:fromYDelta="0" android:toXDelta="100" android:toYDelta="100" /></set>
注意:
1:View动画是对View的影像操作,并不能真正改变view的位置参数和宽高,如果希望动画完之后的状态可以保留必须将fillAfter属性设为true,否则动画在完成后的一瞬间就会恢复原样。
2:平移后的view不能响应view上的事件,因为我们操作的是view的副本,在系统看来实际的view还是在原来的位置。
View.scrollto()和View.scrollby()方法
我们来看一下这两个函数的签名:
public void scrollTo(int x, int y) { if (mScrollX != x || mScrollY != y) { int oldX = mScrollX; int oldY = mScrollY; mScrollX = x; mScrollY = y; invalidateParentCaches(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (!awakenScrollBars()) { postInvalidateOnAnimation(); } } }
public void scrollBy(int x, int y) { scrollTo(mScrollX + x, mScrollY + y); }
注意:
1:scrollTo和scrollBy只能改变View内容的位置,不能改变View在布局中的位置
2:scrollTo是基于所传递参数的绝对滑动,scrollBy是相对于当前位置的相对滑动。
3:从函数中可以看出scrollBy实际上是调用了scrollTo方法。
4:mScrollX = View左边缘和View内容的左边缘的水平距离
mScrollY = Viewe上边缘和View内容的上边缘在竖直方向上的距离
view的边缘是指view的位置,由四个顶点组成。
view内容的边缘时指view中内容的边缘
5:如果view内容向view边界的左上角移动,则mScrollX和mScrollY为正,向左下角移动为负。
实现View中的内容跟随手指移动
package com.example.myscroller;import android.content.Context;import android.util.AttributeSet;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.View;import android.widget.TextView;/** * Created by pingkun.huang on 2016/4/20. */public class MyView extends TextView implements View.OnTouchListener,GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener{ GestureDetector gestureDetector; Context context; public MyView(Context context) { super(context); initData(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); initData(context); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initData(context); } private void initData(Context context) { this.setText("测试文本"); this.setTextSize(30); this.context = context; this.setOnTouchListener(this); super.setClickable(true); super.setLongClickable(true); super.setFocusable(true); gestureDetector = new GestureDetector(context, this); gestureDetector.setOnDoubleTapListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } @Override public boolean onDown(MotionEvent e) { int x = (int) e.getX(); int y = (int) e.getY(); this.scrollTo(-x,-y); return true; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { int x = (int) e2.getX(); int y = (int) e2.getY(); this.scrollTo(-x,-y); //this.scrollBy((int)distanceX,(int)distanceY); return true; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { return true; } @Override public boolean onDoubleTap(MotionEvent e) { this.setTextSize(this.getTextSize()+2); return true; } @Override public boolean onDoubleTapEvent(MotionEvent e) { return true; }}
各种方法的比较
scrollTo和scrollBy适合View的内容滑动。
动画滑动:适合没有交互的View和复杂的动画效果。
布局参数:适用于有交互的View。
- Android--自定义View滑动的六种方法
- android view实现滑动的六种方法
- Android View体系(二)实现View滑动的六种方法
- Android View体系(二)实现View滑动的六种方法
- Androin学习笔记五十三: Android中实现view可以滑动的六种方法
- android view滑动的几种方法
- Android View移动的六种方法
- 实现View滑动的六种方式
- Android 自定义View (六)
- 【Android View事件(四)】View滑动与实现滑动的几种方法
- Android 自定义View:实现View的滑动效果
- Android 自定义View:实现View的滑动效果
- Android实现View滑动的五种方法
- android实现view滑动的7种方法
- Android 实现 View 滑动的七种方法
- Android View滑动的方法总结
- Android自定义View的方法
- Android自定义View的方法
- Android中framework层的cpp文件中调用native层c函数的方法
- 华为软件机试体验
- JSP的四种作用域
- Python工具包安装
- PHP反射机制
- Android--自定义View滑动的六种方法
- linux内核中链表结构及使用方法
- EAccessViolation 地址访问错误
- 为什么我偏爱用GitHub来写书?
- ASCII Unicode 转义字符 UTF-X 释疑
- 机器学习算法小结
- 翻译漫谈笔记之1翻译概论
- Atitit.Base64编码原理与实现设计
- LeetCode 79. Word Search