• 51.自定义布局-SlidingLayout


    涉及知识点:
    1.View绘制三部曲(onMeasure、onLayout、Draw)(第一步和第三步本控件未做处理)
    2.ScrollBy相对滚动、ScrollTo绝对滚动、Scroller滚动器的应用
    3.TouchEvent处理三部曲(dispatch分发、intercept拦截、onTouchEvent处理)(第一步本控件未做处理)
    4.自定义回调接口

    效果图:


    控件源码:
    public class SlidingLayout extends RelativeLayout {
    private Scroller scroller;//滚动器

    /**
    * 构造器
    *
    * @param context
    * @param attrs
    */
    public SlidingLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    mLog.setLogFlag(false);
    initView();
    }

    /**
    * 初始化视图
    */
    private void initView() {
    scroller = new Scroller(getContext(), new DecelerateInterpolator());
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    screenWidth = getMeasuredWidth();
    }

    List<Integer> childLeft;//记录每个子视图的左边界

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    childLeft = new ArrayList<Integer>();

    int left = 0;
    for (int i = 0; i < getChildCount(); i++) {
    View child = getChildAt(i);
    childLeft.add(left);
    child.layout(left, 0, left + child.getMeasuredWidth(), child.getMeasuredHeight());
    left += child.getMeasuredWidth();//宽度累加
    }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
    return super.dispatchTouchEvent(ev);
    }

    float downX, downY, moveX, moveY, upX, upY;
    int disX, disY;//绝对偏移值(move相对于down

    float lastX, lastY;
    int disOffX, disOffY;//相对偏移值(move相对于上一次move)


    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {

    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
    downX = event.getX();
    downY = event.getY();
    mLog.e("ACTION_DOWN:downX=" + downX + " downY=" + downY);
    doDown();
    break;

    case MotionEvent.ACTION_MOVE:
    moveX = event.getX();
    moveY = event.getY();
    disX = (int) (moveX - downX);
    disY = (int) (moveY - downY);
    //如果是左右滑动就拦截
    if (Math.abs(disX) > Math.abs(disY)) {
    return true;
    }
    }
    return super.onInterceptTouchEvent(event);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
    downX = event.getX();
    downY = event.getY();
    mLog.e("ACTION_DOWN:downX=" + downX + " downY=" + downY);
    doDown();
    break;

    case MotionEvent.ACTION_MOVE:
    moveX = event.getX();
    moveY = event.getY();
    disX = (int) (moveX - downX);
    disY = (int) (moveY - downY);

    if (lastX != 0 && lastY != 0) {
    disOffX = (int) (moveX - lastX);
    disOffY = (int) (moveY - lastY);
    }
    lastX = moveX;
    lastY = moveY;
    mLog.e("ACTION_MOVE:moveX=" + moveX + " moveY=" + moveY + " disX=" + disX + " disY=" + disY + " disOffX=" + disOffX + " disOffY=" + disOffY);
    doMove();
    break;

    case MotionEvent.ACTION_UP:
    upX = event.getX();
    upY = event.getY();
    disX = (int) (upX - downX);
    disY = (int) (upY - downY);
    lastX = 0;
    lastY = 0;
    mLog.e("ACTION_UP:upX=" + upX + " upY=" + upY + " disX=" + disX + " disY=" + disY);
    doUp();
    break;

    }

    return true;
    }

    private void doDown() {
    }

    int screenIndex;//记录当前是哪一个子视图

    private void doMove() {
    mLog.e("doMovedisOffX=" + disOffX + " getScrollX=" + getScrollX());


    //如果是第一屏,并且偏移值向右,就什么都不做
    if (screenIndex == 0 && disOffX - getScrollX() > 0) {

    return;
    }
    //如果是最后一屏,并且偏移值向左,就什么都不做
    if (screenIndex == getChildCount() - 1 && disOffX - getScrollX() < -childLeft.get(getChildCount() - 1)) {
    return;
    }
    /**
    * 特别注意:视图的滑动值和手势的偏移值是相反的!!!
    */
    scrollBy(-disOffX, 0);
    }

    private void doUp() {
    mLog.e("doUp:disOffX=" + disOffX + " getMeasuredWidth/2=" + getMeasuredWidth() / 2);

    // //向右偏移==向左滑动,大于半屏时,跳到上一屏
    if (disX >= getMeasuredWidth() / 2 && screenIndex > 0) {
    mLog.e("跳到上一屏");
    screenIndex--;
    }
    // //向左偏移==向右滑动,大于半屏时,跳到下一屏
    else if (disX <= -getMeasuredWidth() / 2 && screenIndex < getChildCount() - 1) {
    mLog.e("跳到下一屏");
    screenIndex++;
    }

    //只有滑动过,才恢复屏幕(避免启动无用的scroller)
    if (getScrollX() > 0) {
    scrollerScreen();
    }

    }

    int scrollTime = 500;

    /**
    * 使用scroller换屏(启动滑动动画,具体滑动需要到computeScroll里面执行)
    */
    private void scrollerScreen() {
    //启动滑动(默认时间为250ms,此处我自定义为1000ms
    scroller.startScroll(getScrollX(), getScrollY(), childLeft.get(screenIndex) - getScrollX(), 0, scrollTime);
    invalidate();
    }


    float screenIndexF;
    int screenWidth;

    /**
    * 绝对滑动(相对于零点的滑动值)
    */
    @Override
    public void scrollTo(int x, int y) {
    super.scrollTo(x, y);
    mLog.e("scrollTox=" + x + " y=" + y);

    }


    /**
    * 相对滑动(相对于上一位置的滑动值)
    */
    @Override
    public void scrollBy(int x, int y) {
    super.scrollBy(x, y);
    mLog.e("scrollByx=" + x + " y=" + y);


    }

    /**
    * 计算滑动值(每一次重绘会自动调用此方法)
    */
    @Override
    public void computeScroll() {
    super.computeScroll();
    mLog.e("computeScroll");

    //当滑动完成时,computeScrollOffset()返回false
    if (scroller.computeScrollOffset()) {
    scrollTo(scroller.getCurrX(), 0);
    invalidate();
    }

    screenIndexF = getScrollX() / (float) screenWidth;
    iScreenIndexListener.currentScreenIndex(screenIndexF);
    mLog.e("screenIndexF=" + screenIndexF);
    }

    /**
    * 自定义回调接口,回调当前的屏幕下标数
    */
    interface IScreenIndexListener {
    void currentScreenIndex(float screenIndexF);
    }

    IScreenIndexListener iScreenIndexListener;

    public void setiScreenIndexListener(IScreenIndexListener iScreenIndexListener) {
    this.iScreenIndexListener = iScreenIndexListener;
    }
    }

    应用实例:
    Java:
    Xml:






  • 相关阅读:
    云计算初探
    MySQL、HBase、ES的特点和区别
    MongoDB、ElasticSearch、Redis、HBase这四种热门数据库的优缺点及应用场景
    主流 Kubernetes 发行版梳理
    如何在flink中传递参数
    (47)zabbix报警媒介:Ez Texting
    (46)zabbix报警媒介:Jabber
    (45)zabbix报警媒介:SMS
    (44)zabbix报警媒介:email
    (43)zabbix报警媒介介绍
  • 原文地址:https://www.cnblogs.com/yutianran/p/5069680.html
Copyright © 2020-2023  润新知