zoukankan      html  css  js  c++  java
  • 高级UI-自定义控件

    自定义控件在Android开发中有着大量的运用,为了做出符合项目的效果很多时候需要自定义控件,这里就使用两个自定义控件,来说明自定义控件的使用流程

    仿QQ侧滑

    之前使用DrawerLayoutNavigationView都实现了侧滑的效果,在这里使用自定义的View完成相同的效果
    这里考虑到的是继承HorizontalScrollView,复写里面的onMeasure方法,设置滑动菜单和主菜单的宽度设置,复写onLayout方法,按照需求摆放子控件,复写onTouchEvent方法,控制移动距离,复写onScrollChanged方法,控制滑动时候的动画
    布局

    <?xml version="1.0" encoding="utf-8"?>
    <com.cj5785.customviewtest.SlidingMenu xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_orange_light"
        android:scrollbars="none">
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal">
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@android:color/holo_green_light"/>
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@android:color/darker_gray"/>
        </LinearLayout>
    
    </com.cj5785.customviewtest.SlidingMenu>
    

    自定义控件

    public class SlidingMenu extends HorizontalScrollView {
        private int mScreenWidth;
        private ViewGroup mMenu;
        private ViewGroup mMain;
        private int mMenuWidth;
        private boolean isOnce;
        private float downX;
    
        public SlidingMenu(Context context, AttributeSet attrs) {
            super(context, attrs);
            //得到屏幕宽度
            WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            DisplayMetrics metrics = new DisplayMetrics();
            wm.getDefaultDisplay().getMetrics(metrics);
            mScreenWidth = metrics.widthPixels;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //只测量一次
            if (!isOnce) {
                //获得侧滑菜单和主菜单
                LinearLayout wrapper = (LinearLayout) getChildAt(0);
                mMenu = (ViewGroup) wrapper.getChildAt(0);
                mMain = (ViewGroup) wrapper.getChildAt(1);
                //得到宽度,为了体验效果,这里一般会设置menu的right padding宽度
                mMenuWidth = mScreenWidth - mScreenWidth / 5;
                mMenu.getLayoutParams().width = mMenuWidth;
                mMain.getLayoutParams().width = mScreenWidth;
            }
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            if (changed) {
                //开始绘制的时候,向右滑动一段距离
                this.scrollTo(mMenuWidth, 0);
                isOnce = true;
            }
            super.onLayout(changed, l, t, r, b);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            int action = ev.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    //按下时的位置
                    downX = ev.getX();
                    break;
                case MotionEvent.ACTION_UP:
                    //松开后滑动的距离,滑动距离小于屏幕的四分之一则回到原位置
                    float dx = ev.getX() - downX;
                    if (dx < mScreenWidth / 4) {
                        this.smoothScrollTo(mMenuWidth, 0);
                    } else {
                        this.smoothScrollTo(0, 0);
                    }
                    return true;
            }
            return super.onTouchEvent(ev);
        }
    
        //当滑动开始时候会调用onScrollChanged()
        @Override
        protected void onScrollChanged(int l, int t, int oldl, int oldt) {
            //在这里设置动画
            //滑动百分比
            float factor = (float) l / mMenuWidth;
            //1.平移效果
            mMenu.setTranslationX(mMenuWidth * factor * 0.7F);
            //2.缩放效果
            mMenu.setScaleX(1 - 0.4F * factor);
            mMenu.setScaleY(1 - 0.4F * factor);
            mMain.setScaleX(0.9F + 0.1F * factor);
            mMain.setScaleY(0.9F + 0.1F * factor);
            //3.透明度效果
            mMenu.setAlpha(1 - factor);
            mMain.setAlpha(0.8F + 0.2F * factor);
            super.onScrollChanged(l, t, oldl, oldt);
        }
    }
    

    实现效果如下
    自定义控件-仿QQ侧滑

    实现条目侧滑的效果

    在QQ中,还有个功能是比较棒的,那就是条目侧滑,那么要怎么实现条目侧滑效果呢
    这里我们使用自定义LinearLayout来实现,其滑动效果通过Scroller来控制
    使用dispatchTouchEvent()来记录DOWN,MOVE,UP事件响应的参数,滑动过程中不断调用computeScroll()移动控件位置,使得其形成动画效果,由于要控制滑动距离,就需要动态测量出滑出的距离,复写onFinishInflate()得到距离
    布局很简单,就是两个TextView

    <?xml version="1.0" encoding="utf-8"?>
    <com.cj5785.customviewtest.SlidingItemMenuLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:background="@android:color/holo_green_light"
            android:gravity="center"
            android:text="This is a item"
            android:textSize="32sp" />
    
        <TextView
            android:layout_width="100dp"
            android:layout_height="80dp"
            android:background="@android:color/holo_red_light"
            android:gravity="center"
            android:text="删除"
            android:textSize="32sp" />
    
    </com.cj5785.customviewtest.SlidingItemMenuLayout>
    

    然后是自定义控件

    public class SlidingItemMenuLayout extends LinearLayout {
        private Scroller mScroller;
        private float startX;
        private float startY;
        private float dx;
        private float dy;
        private View rightChild;
    
        public SlidingItemMenuLayout(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            setOrientation(LinearLayout.HORIZONTAL);
            //用来设置松开回弹
            mScroller = new Scroller(getContext(), new AccelerateInterpolator(), true);
        }
    
    	//绘制完成后调用
        @Override
        protected void onFinishInflate() {
            super.onFinishInflate();
            rightChild = getChildAt(1);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //记录按下时位置
                    startX = ev.getX();
                    startY = ev.getY();
                    //按下以后拦截事件,交由父类处理
                    super.dispatchTouchEvent(ev);
                    return true;
                case MotionEvent.ACTION_MOVE:
                    dx = ev.getX() - startX;
                    dy = ev.getY() - startY;
                    //系统能检测到的最小距离
                    if (Math.abs(dx) - Math.abs(dy) > ViewConfiguration.getTouchSlop()) {
                        //向左滑动距离不能大于最右边,且向右滑动距离不能大于零
                        if (getScrollX() + (-dx) > rightChild.getWidth()
                                || getScrollX() + (-dx) < 0) {
                            return true;
                        }
                        //滑动一段距离,重新记录位置,拦截滑动事件
                        scrollBy((int) -dx, 0);
                        startX = ev.getX();
                        startY = ev.getY();
                        return true;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    //得到松手时的偏移量,超过一半则全部显示,否则不显示
                    int offset = getScrollX() / (float) rightChild.getWidth() > 0.5 ?
                            rightChild.getWidth() - getScrollX() : -getScrollX();
                    //初始化滑动参数
                    mScroller.startScroll(getScrollX(), getScrollY(), offset, 0);
                    //重新绘制测量,此时会调用computeScroll()方法
                    invalidate();
                    //参数重新赋值
                    startX = 0;
                    startY = 0;
                    dx = 0;
                    dy = 0;
                    break;
                default:
                    break;
            }
            return super.dispatchTouchEvent(ev);
        }
    
        //开启滑动以后,就会不断调用此方法
        @Override
        public void computeScroll() {
            if (mScroller.computeScrollOffset()) {
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
                postInvalidate();
            }
            super.computeScroll();
        }
    }
    

    实现效果如下
    自定义控件-Item侧滑

  • 相关阅读:
    JAVA学习---文件和流
    JAVA学习---集合和工具类
    JAVA学习---异常
    python-time模块
    python-并发编程
    python-网络编程
    python-并发编程之进程
    python-面向对象之多态
    python-面向对象之反射
    python-面向对象之封装
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664585.html
Copyright © 2011-2022 走看看