子视图match_parent在自定义视图中使用viewdraghelper忽略父级

时间:2017-02-01 21:16:49

标签: java android viewgroup viewdraghelper

我最近一直在研究自定义视图,并试图通过位于此处的教程制作一个具体示例:http://flavienlaurent.com/blog/2013/08/28/each-navigation-drawer-hides-a-viewdraghelper/

我将添加相关代码以防止死链接丢失信息。本教程的作者解释了如何创建类似于YouTube的Android应用播放器行为的ViewDragHelper,这些是他提供的代码

activity_main.xml

<FrameLayout

    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:tag="list"
        />

<com.example.vdh.YoutubeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/youtubeLayout"
        android:orientation="vertical"
        android:visibility="visible">

    <TextView
            android:id="@+id/viewHeader"
            android:layout_width="match_parent"
            android:layout_height="128dp"
            android:fontFamily="sans-serif-thin"
            android:textSize="25sp"
            android:tag="text"
            android:gravity="center"
            android:textColor="@android:color/white"
            android:background="#AD78CC"/>

    <TextView
            android:id="@+id/viewDesc"
            android:tag="desc"
            android:textSize="35sp"
            android:gravity="center"
            android:text="Loreum Loreum"
            android:textColor="@android:color/white"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#FF00FF"/>

</com.example.vdh.YoutubeLayout>

YoutubeLayout.java

public class YoutubeLayout extends ViewGroup {

    private final ViewDragHelper mDragHelper;

    private View mHeaderView;
    private View mDescView;

    private float mInitialMotionX;
    private float mInitialMotionY;

    private int mDragRange;
    private int mTop;
    private float mDragOffset;


    public YoutubeLayout(Context context) {
      this(context, null);
    }

    public YoutubeLayout(Context context, AttributeSet attrs) {
      this(context, attrs, 0);
    }

    @Override
    protected void onFinishInflate() {
        mHeaderView = findViewById(R.id.viewHeader);
        mDescView = findViewById(R.id.viewDesc);
    }

    public YoutubeLayout(Context context, AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
      mDragHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback());
    }

    public void maximize() {
        smoothSlideTo(0f);
    }

    boolean smoothSlideTo(float slideOffset) {
        final int topBound = getPaddingTop();
        int y = (int) (topBound + slideOffset * mDragRange);

        if (mDragHelper.smoothSlideViewTo(mHeaderView, mHeaderView.getLeft(), y)) {
            ViewCompat.postInvalidateOnAnimation(this);
            return true;
        }
        return false;
    }

    private class DragHelperCallback extends ViewDragHelper.Callback {

      @Override
      public boolean tryCaptureView(View child, int pointerId) {
            return child == mHeaderView;
      }

        @Override
      public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
          mTop = top;

          mDragOffset = (float) top / mDragRange;

            mHeaderView.setPivotX(mHeaderView.getWidth());
            mHeaderView.setPivotY(mHeaderView.getHeight());
            mHeaderView.setScaleX(1 - mDragOffset / 2);
            mHeaderView.setScaleY(1 - mDragOffset / 2);

            mDescView.setAlpha(1 - mDragOffset);

            requestLayout();
      }

      @Override
      public void onViewReleased(View releasedChild, float xvel, float yvel) {
          int top = getPaddingTop();
          if (yvel > 0 || (yvel == 0 && mDragOffset > 0.5f)) {
              top += mDragRange;
          }
          mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
      }

      @Override
      public int getViewVerticalDragRange(View child) {
          return mDragRange;
      }

      @Override
      public int clampViewPositionVertical(View child, int top, int dy) {
          final int topBound = getPaddingTop();
          final int bottomBound = getHeight() - mHeaderView.getHeight() - mHeaderView.getPaddingBottom();

          final int newTop = Math.min(Math.max(top, topBound), bottomBound);
          return newTop;
      }

    }

    @Override
    public void computeScroll() {
      if (mDragHelper.continueSettling(true)) {
          ViewCompat.postInvalidateOnAnimation(this);
      }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
      final int action = MotionEventCompat.getActionMasked(ev);

      if (( action != MotionEvent.ACTION_DOWN)) {
          mDragHelper.cancel();
          return super.onInterceptTouchEvent(ev);
      }

      if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
          mDragHelper.cancel();
          return false;
      }

      final float x = ev.getX();
      final float y = ev.getY();
      boolean interceptTap = false;

      switch (action) {
          case MotionEvent.ACTION_DOWN: {
              mInitialMotionX = x;
              mInitialMotionY = y;
                interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
              break;
          }

          case MotionEvent.ACTION_MOVE: {
              final float adx = Math.abs(x - mInitialMotionX);
              final float ady = Math.abs(y - mInitialMotionY);
              final int slop = mDragHelper.getTouchSlop();
              if (ady > slop && adx > ady) {
                  mDragHelper.cancel();
                  return false;
              }
          }
      }

      return mDragHelper.shouldInterceptTouchEvent(ev) || interceptTap;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
      mDragHelper.processTouchEvent(ev);

      final int action = ev.getAction();
        final float x = ev.getX();
        final float y = ev.getY();

        boolean isHeaderViewUnder = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
        switch (action & MotionEventCompat.ACTION_MASK) {
          case MotionEvent.ACTION_DOWN: {
              mInitialMotionX = x;
              mInitialMotionY = y;
              break;
          }

          case MotionEvent.ACTION_UP: {
              final float dx = x - mInitialMotionX;
              final float dy = y - mInitialMotionY;
              final int slop = mDragHelper.getTouchSlop();
              if (dx * dx + dy * dy < slop * slop && isHeaderViewUnder) {
                  if (mDragOffset == 0) {
                      smoothSlideTo(1f);
                  } else {
                      smoothSlideTo(0f);
                  }
              }
              break;
          }
      }


      return isHeaderViewUnder && isViewHit(mHeaderView, (int) x, (int) y) || isViewHit(mDescView, (int) x, (int) y);
    }


    private boolean isViewHit(View view, int x, int y) {
        int[] viewLocation = new int[2];
        view.getLocationOnScreen(viewLocation);
        int[] parentLocation = new int[2];
        this.getLocationOnScreen(parentLocation);
        int screenX = parentLocation[0] + x;
        int screenY = parentLocation[1] + y;
        return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() &&
                screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
        int maxHeight = MeasureSpec.getSize(heightMeasureSpec);

        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0),
                resolveSizeAndState(maxHeight, heightMeasureSpec, 0));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
      mDragRange = getHeight() - mHeaderView.getHeight();

        mHeaderView.layout(
                0,
                mTop,
                r,
                mTop + mHeaderView.getMeasuredHeight());

        mDescView.layout(
                0,
                mTop + mHeaderView.getMeasuredHeight(),
                r,
                mTop  + b);
    }
}

作者指出onLayoutonMeasure编写得很糟糕,我认为这些(或其中一个)可能是导致其中一个孩子出现问题的原因。

对于我的目标,我将mDescView替换为包含相应Framelayout的{​​{1}}视图。它们的高度都设置为TextView,父级(match_parent)的高度设置正确,但其子级(mDescView内的TextView)会忽略父级高度伸展以适应其高度等于屏幕高度(或自定义视图高度,不能说明差异)。这是一个问题,因为mDescView孩子永远不会根据父母通过mDescView正确调整他们的身高,我一直在寻找解决方案几天,但没有找到任何解决方案,通过研究我无法&#39找到发生这种情况的原因。

这是此问题的结果,请注意match_parent高度大于其父TextView的高度,尽管它们的高度都设置为mDescView

enter image description here

所以我的问题是,如何让父母(或任何父母)的孩子(孩子)与他们的父母相匹配?高度就像他们应该的那样。

作为额外的请求,如果可能,任何人都可以解释为什么作者认为他的某些方法不是最好的/正确的,以及如何正确/更好地完成它们。

1 个答案:

答案 0 :(得分:0)

我终于纠正了这个问题,虽然我仍然不知道这是否是正确的方法。我的解决方案是设置两个视图&#39; onLayout期间的身高 我还将此设置为仅在第一次调用此方法时运行,因此特定语句仅运行一次且仅在第一次运行时(firstRuntrue时)

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    mDragRange = getHeight() - mHeaderView.getHeight();

    if (firstRun) {
        firstRun = false;
        mDescView.getLayoutParams().height = getHeight() - mHeaderView.getMeasuredHeight();
    }

    mHeaderView.layout(
            0,
            mTop,
            r,
            mTop + mHeaderView.getMeasuredHeight());

    mDescView.layout(
            0,
            mTop + mHeaderView.getMeasuredHeight(),
            r,
            mTop  + b);
}