PS:本文系转载文章,浏览原文可读性会更好,文章开端有原文链接
ps:本篇文章的 demo 是用 AndroidStudio 工具开发的。这里在 Android 中的自定义 View(一)这篇文章的根底上再持续写一下自定义 View 的案例,这里的自定义 ViewGroup 间接继承于 ViewGroup,咱们写一个相似 ViewPager 这样的自定义 ViewGroup,能够让它左右滑动。(1)新建一个 Activity,名叫 DemoActivity;public class DemoActivity extends Activity {private MyViewPager mViewPager; @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); setContentView(R.layout.activity_demo); initView();} private void initView() { LayoutInflater inflater = getLayoutInflater(); mViewPager = (MyViewPager) findViewById(R.id.viewPager); //1、final int screenWidth = getScreenMetrics(this).widthPixels; for (int i = 0; i < 4; i++) {View layout =inflater.inflate( R.layout.content_layout, mViewPager, false); layout.getLayoutParams().width = screenWidth; TextView textView = (TextView) layout.findViewById(R.id.tv); textView.setText(“ 第 ” +(i+1) + “ 页面 ”); //2、mViewPager.addView(layout); } } public DisplayMetrics getScreenMetrics(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(dm); return dm; }} 正文 1 示意获取手机屏幕的宽度;正文 2 示意将创立的 View 增加到 MyViewPager 中。(2)新建一个布局文件 activity_demo.xml;<LinearLayout 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” android:background=”#ffffff” android:orientation=”vertical” > <com.xiaoer.MyViewPager android:id=”@+id/viewPager” android:layout_width=”wrap_content” android:layout_height=”match_parent” /></LinearLayout>(3)新建一个类 MyViewPager 并继承于 ViewGroup;public class MyViewPager extends ViewGroup {private int mChildrenSize; private int mChildWidth; private int mChildIndex; private int mLastX = 0; private int mLastY = 0; private int mLastXIntercept = 0; private int mLastYIntercept = 0; private Scroller mScroller; private VelocityTracker mVelocityTracker; public MyViewPager(Context context) {super(context); init();} public MyViewPager(Context context, AttributeSet attrs) {super(context, attrs); init();} public MyViewPager(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle); init();} private void init() { if (mScroller == null) {mScroller = new Scroller(getContext()); mVelocityTracker = VelocityTracker.obtain();} } //3、@Override public boolean onInterceptTouchEvent(MotionEvent event) {boolean intercepted = false; int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) {case MotionEvent.ACTION_DOWN: { intercepted = false; if (!mScroller.isFinished()) {mScroller.abortAnimation(); //4、intercepted = true; } break; } case MotionEvent.ACTION_MOVE: {int deltaX = x – mLastXIntercept; int deltaY = y – mLastYIntercept; if (Math.abs(deltaX) > Math.abs(deltaY)) {//5、intercepted = true;} else {intercepted = false;} break; } case MotionEvent.ACTION_UP: {intercepted = false; break;} default: break; } mLastX = x; mLastY = y; mLastXIntercept = x; mLastYIntercept = y; return intercepted; } @Override public boolean onTouchEvent(MotionEvent event) {mVelocityTracker.addMovement(event); int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) {case MotionEvent.ACTION_DOWN: { if (!mScroller.isFinished()) {mScroller.abortAnimation(); } break; } case MotionEvent.ACTION_MOVE: {int deltaX = x – mLastX; int deltaY = y – mLastY; scrollBy(-deltaX, 0); break; } case MotionEvent.ACTION_UP: {int scrollX = getScrollX(); //6、mVelocityTracker.computeCurrentVelocity(1000); //7、float xVelocity = mVelocityTracker.getXVelocity(); if (Math.abs(xVelocity) >= 50) {mChildIndex = xVelocity > 0 ? mChildIndex – 1 : mChildIndex + 1;} else {mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth; } mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize – 1)); //8、int dx = mChildIndex mChildWidth – scrollX; smoothScrollBy(dx, 0); mVelocityTracker.clear(); break;} default: break; } mLastX = x; mLastY = y; return true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec); int measuredWidth = 0; int measuredHeight = 0; final int childCount = getChildCount(); measureChildren(widthMeasureSpec, heightMeasureSpec); int maxChildWidth = 0; int maxChildHeight = 0; //14、for (int i = 0; i < childCount; i++) {View child = getChildAt(i); if (child.getMeasuredHeight() > maxChildHeight) {maxChildHeight = child.getMeasuredHeight(); } if (child.getMeasuredWidth() > maxChildWidth) {maxChildWidth = child.getMeasuredWidth(); } } int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); if (childCount == 0) {setMeasuredDimension(0, 0); } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {measuredWidth = maxChildWidth childCount; measuredHeight = maxChildHeight; //15、setMeasuredDimension(measuredWidth, measuredHeight); } else if (heightSpecMode == MeasureSpec.AT_MOST) {final View childView = getChildAt(0); measuredHeight = maxChildHeight; //16、setMeasuredDimension(widthSpaceSize, childView.getMeasuredHeight()); } else if (widthSpecMode == MeasureSpec.AT_MOST) {measuredWidth = maxChildWidth * childCount; //17、setMeasuredDimension(measuredWidth, heightSpaceSize); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) {int childLeft = 0; final int childCount = getChildCount(); mChildrenSize = childCount; for (int i = 0; i < childCount; i++) {final View childView = getChildAt(i); if (childView.getVisibility() != View.GONE) {final int childWidth = childView.getMeasuredWidth(); mChildWidth = childWidth; //18、childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight()); childLeft += childWidth; } } } private void smoothScrollBy(int dx, int dy) {//9、mScroller.startScroll(getScrollX(), 0, dx, 0, 500); //10、invalidate();} //11、@Override public void computeScroll() { if (mScroller.computeScrollOffset()) {//12、scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //13、postInvalidate();} } @Override protected void onDetachedFromWindow() { mVelocityTracker.recycle(); super.onDetachedFromWindow();}} 正文 3 的 onInterceptTouchEvent 办法是是否拦挡子元素触摸的办法,如果返回值是 true,那么就拦挡子元素触摸;如果返回值是 false,那么就不拦挡子元素触摸。正文 4 和正文 5 的代码都示意拦挡子元素触摸;正文 6 示意 1s 的工夫内运行来多少个像素点;正文 7 示意获取 X 轴上的速率;正文 8 示意获取 X 轴上挪动的间隔;正文 9 中的 mScroller 是一个辅助动画的类,startScroll 办法并没有开始挪动,只是将该办法的参数保留到 mScroller 这个对象中。正文 10 的办法执行后最终调用正文 11 的办法;正文 12 的办法最终实现一步步的挪动;正文 13 的代码执行后最终调用正文 11 的代码,直到正文 6 代码中的 mVelocityTracker.computeCurrentVelocity(1000) 这个参数 1000 毫秒过后,就不在调用正文 13 的代码了,也就是 mScroller.computeScrollOffset() 为 false。正文 14 的 for 循环是为了获取子 View 的最大宽度和最大高度;正文 15,当 MyViewPager 宽和高的模式为 MeasureSpec.AT_MOST 时,将子 View 中最大的宽度乘积子 View 的个数作为 MyViewPager 的宽度,将子 View 中最大的高度作为 MyViewPager 的高度。正文 16,当 MyViewPager 高的模式为 MeasureSpec.AT_MOST 时,将子 View 中最大的高度作为 MyViewPager 的高度;正文 17,当 MyViewPager 宽的模式为 MeasureSpec.AT_MOST 时,将子 View 中最大的宽度乘积子 View 的个数作为 MyViewPager 的宽度;正文 18,执行子 View 的 layout 过程。程序运行后果如下所示;
第一次用手指向左滑动就能够看到成果了。下面的代码并标准,第一点是没有子元素的时候不应该间接把宽和高设为 0,而应该依据 LayoutParams 中的宽和高来做相应解决,第二点是在测量 MyViewPager 的宽和高时没有思考到它的 padding 以及子元素的 margin,因为它的 padding 以及子元素的 margin 会影响到 MyViewPager 的宽和高。