Android中的自定义View(二)

文章收藏的好句子:方向对了,慢其实就是快。

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 过程。

程序运行结果如下所示;

c6deeabc613acd113e3257032f871ec8.png

第一次用手指向左滑动就可以看到效果了。

上面的代码并规范,第一点是没有子元素的时候不应该直接把宽和高设为0,

而应该根据 LayoutParams 中的宽和高来做相应处理,第二点是在测量 MyViewPager 的宽和高时没有考虑到它的 padding 以及子元素的 margin,因为它的 padding 以及子元素的 margin会影响到 MyViewPager 的宽和高。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值