【问题标题】:Children match_parent ignoring parent in custom view with viewdraghelper使用 viewdraghelper 的自定义视图中的子 match_parent 忽略父级
【发布时间】:2017-02-01 21:16:48
【问题描述】:

我最近一直在研究自定义视图,并试图从位于此处的教程中制作一个具体示例: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 替换为包含相应TextViewFramelayout 视图。他们两个都将高度设置为match_parent,并且父级(mDescView)确实正确设置了它的高度,但是它的子级(TextView inside mDescView)忽略了父级高度并拉伸以使其高度等于屏幕高度(或自定义视图高度,无法区分)。这是一个问题,因为mDescView 孩子永远不会通过match_parent 根据父母正确调整他们的身高,我一直在寻找解决方案好几天,但没有找到,通过研究我找不到原因为什么会这样。

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

所以我的问题是,我怎样才能让那个父母(或任何父母)的孩子(孩子们)像他们应该的那样匹配他们父母的身高。

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

【问题讨论】:

    标签: java android viewgroup viewdraghelper


    【解决方案1】:

    我终于纠正了这个问题,尽管我仍然不知道这是否是正确的方法。我的解决方案是在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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-25
      相关资源
      最近更新 更多