我试图只使用DatePickerTextViewEditText来实现类似ImageView的行为。你可以在下面的图片上看到结果:
我这样做的原因很简单:我无法将常规的DatePicker样式设置为上面图片中的那种样式。我现在有了我想要的外观,我还实现了一种滚动,但还有一些问题。例如,我们在照片上显示的第一个日期的第一年。在那里你可以看到一个上箭头(ImageView)、一年(EditText)和一个下箭头(ImageView),它们都嵌套在一个垂直的LinearLayout中。
我现在为每个布局添加了一个手势识别器:

public class MyClass extends DialogFragment implements OnTouchListener, OnGestureListener {

    ...

    private GestureDetector gestureDetector = null;

    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this.setRetainInstance(true);
        this.gestureDetector = new GestureDetector(this.getActivity(), this);
    }

    ...

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        ...

        this.layoutFromDay = (LinearLayout) view.findViewById(R.id.date_selection_layout_from_day);
        this.layoutFromDay.setOnTouchListener(this);
        this.layoutFromMonth = (LinearLayout) view.findViewById(R.id.date_selection_layout_from_month);
        this.layoutFromMonth.setOnTouchListener(this);
        this.layoutFromYear = (LinearLayout) view.findViewById(R.id.date_selection_layout_from_year);
        this.layoutFromYear.setOnTouchListener(this);
        this.layoutToDay = (LinearLayout) view.findViewById(R.id.date_selection_layout_to_day);
        this.layoutToDay.setOnTouchListener(this);
        this.layoutToMonth = (LinearLayout) view.findViewById(R.id.date_selection_layout_to_month);
        this.layoutToMonth.setOnTouchListener(this);
        this.layoutToYear = (LinearLayout) view.findViewById(R.id.date_selection_layout_to_year);
        this.layoutToYear.setOnTouchListener(this);

        ...
    }

    ...

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        motionEvent.setSource(view.getId());

        return this.gestureDetector.onTouchEvent(motionEvent);
    }

    ...

    @Override
    public boolean onDown(MotionEvent motionEvent) {
        return true;
    }

    @Override
    public boolean onFling(MotionEvent motionEvent1, MotionEvent motionEvent2, float distanceX, float distanceY) {
        return true;
    }

    @Override
    public void onLongPress(MotionEvent motionEvent) {
        return;
    }

    @Override
    public boolean onScroll(MotionEvent motionEvent1, MotionEvent motionEvent2, float velocityX, float velocityY) {
        LinearLayout sourceView = this.getSourceView(motionEvent1.getSource());

        Log.v("DataSelectionDialogFragment", "OnScroll Called");

        if(sourceView != null) {

            Log.v("DataSelectionDialogFragment", "SouceView not null");
            if(motionEvent1.getY() > motionEvent2.getY() && velocityY > 10) {

                Log.v("DataSelectionDialogFragment", "Scroll Up");
                if(sourceView.equals(this.layoutFromDay)) {
                    int currentValue = Integer.parseInt(this.editTextFromDay.getText().toString());

                    if(currentValue < 31) {
                        this.editTextFromDay.setText("" + ++currentValue);
                    }
                } else if(sourceView.equals(layoutToDay)) {
                    int currentValue = Integer.parseInt(this.editTextToDay.getText().toString());

                    if(currentValue < 31) {
                        this.editTextToDay.setText("" + ++currentValue);
                    }
                } else if(sourceView.equals(layoutFromMonth)) {
                    int currentValue = Integer.parseInt(this.editTextFromMonth.getText().toString());

                    if(currentValue < 12) {
                        this.editTextFromMonth.setText("" + ++currentValue);
                    }
                } else if(sourceView.equals(layoutToMonth)) {
                    int currentValue = Integer.parseInt(this.editTextToMonth.getText().toString());

                    if(currentValue < 12) {
                        this.editTextToMonth.setText("" + ++currentValue);
                    }
                } else if(sourceView.equals(layoutFromYear)) {
                    int currentValue = Integer.parseInt(this.editTextFromYear.getText().toString());

                    this.editTextFromYear.setText("" + ++currentValue);
                } else if(sourceView.equals(layoutToYear)) {
                    int currentValue = Integer.parseInt(this.editTextToYear.getText().toString());

                    this.editTextToYear.setText("" + ++currentValue);
                }
            } else if(motionEvent1.getY() < motionEvent2.getY() && velocityY < 10) {
                Log.v("DataSelectionDialogFragment", "Scroll Down");

                if(sourceView.equals(this.layoutFromDay)) {
                    int currentValue = Integer.parseInt(this.editTextFromDay.getText().toString());

                    if(currentValue > 1) {
                        this.editTextFromDay.setText("" + --currentValue);
                    }
                } else if(sourceView.equals(layoutToDay)) {
                    int currentValue = Integer.parseInt(this.editTextToDay.getText().toString());

                    if(currentValue > 1) {
                        this.editTextToDay.setText("" + --currentValue);
                    }
                } else if(sourceView.equals(layoutFromMonth)) {
                    int currentValue = Integer.parseInt(this.editTextFromMonth.getText().toString());

                    if(currentValue > 1) {
                        this.editTextFromMonth.setText("" + --currentValue);
                    }
                } else if(sourceView.equals(layoutToMonth)) {
                    int currentValue = Integer.parseInt(this.editTextToMonth.getText().toString());

                    if(currentValue > 1) {
                        this.editTextToMonth.setText("" + --currentValue);
                    }
                } else if(sourceView.equals(layoutFromYear)) {
                    int currentValue = Integer.parseInt(this.editTextFromYear.getText().toString());

                    if(currentValue > 1970) {
                        this.editTextFromYear.setText("" + --currentValue);
                    }
                } else if(sourceView.equals(layoutToYear)) {
                    int currentValue = Integer.parseInt(this.editTextToYear.getText().toString());

                    if(currentValue > 1970) {
                        this.editTextToYear.setText("" + --currentValue);
                    }
                }
            }
        }

        return true;
    }

    @Override
    public void onShowPress(MotionEvent motionEvent) {
        return;
    }

    @Override
    public boolean onSingleTapUp(MotionEvent motionEvent) {
        return true;
    }

    ...

    private LinearLayout getSourceView(int source) {
        switch(source) {
        case R.id.date_selection_layout_from_day:
            return this.layoutFromDay;
        case R.id.date_selection_layout_from_month:
            return this.layoutFromMonth;
        case R.id.date_selection_layout_from_year:
            return this.layoutFromYear;
        case R.id.date_selection_layout_to_day:
            return this.layoutToDay;
        case R.id.date_selection_layout_to_month:
            return this.layoutToMonth;
        case R.id.date_selection_layout_to_year:
            return this.layoutToYear;
        }

        return null;
    }
}

如你所见,我给每个OnTouchListener加了一个LinearLayout。在onTouch方法中,我将源布局的id保存到MotionEvent中。
在我的onScroll方法中,我现在可以找出哪个LinearLayout是滚动事件的源。比较第一个和第二个MotionEvent的y值,我现在可以决定是向下滚动还是向上滚动,并且可以更改EditText中的值。
现在我的问题是:
滚动正在工作…但是…并不总是。这很难解释,有时我滚动10秒或更长时间,什么也没有发生,有时滚动只是瞬间发生。我找不到它什么时候起作用,什么时候不起作用的模式,所以我必须在这里寻求帮助。我想我已经做了很好的工作,只差一英寸就可以达到我的目标。
我希望有人能帮我解决这个问题。
编辑-2
我能分解代码使之更容易理解。采用以下布局:
<ScrollView 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:layout_gravity="center"
    android:fillViewport="true"
    android:gravity="center" >

    <LinearLayout
        android:id="@+id/date_selection_layout_from_day"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="vertical" >

        <EditText
            android:id="@+id/date_selection_edittext_from_day"
            android:layout_width="100dp"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:background="@drawable/custom_background"
            android:gravity="center"
            android:inputType="number"
            android:maxLines="1"
            android:nextFocusDown="@+id/date_selection_edittext_from_month"
            android:paddingBottom="5dp"
            android:paddingTop="5dp"
            android:singleLine="true"
            android:textColor="@color/textcolorprimary"
            android:textSize="20sp" />
    </LinearLayout>

</ScrollView>

当我将OnTouchListener设置为我的EditText时:
this.editTextFromDay = (EditText) view.findViewById(R.id.date_selection_edittext_from_day);
this.editTextFromDay.setOnTouchListener(this);

一切都像是一种魅力,但当我把它设置为布局时:
this.layoutFromDay = (LinearLayout) view.findViewById(R.id.date_selection_layout_from_day);
this.layoutFromDay.setOnTouchListener(this);

this.editTextFromDay = (EditText) view.findViewById(R.id.date_selection_edittext_from_day);
this.editTextFromDay.setEnabled(false);

当我滚动到EditText时,ontouch从来没有反应。这意味着EditText正在消耗OnTouch即使它被禁用。所以我现在的问题变为:如何让我的子视图忽略触摸事件并将其重定向到父布局?我希望布局中的所有触摸事件都从布局调用,而不是从其子视图调用。
编辑3
我不能使用onInterceptTouchEvent(MotionEvent ev),因为我的代码在
DialogFragment来自支持包。

最佳答案

我有一个建议给你,这听起来像是一个解决办法,但可以让你实现你的目标。
1)在当前层次结构中的所有其他元素之上注入rawView。这将是你的覆盖触摸层。

 <!-- Other XML childs here -->

<View
    android:id="@+id/touch_layer"
    android:clickable="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

2)获取触摸图层的参考并附加自定义OnTouchListener
touchLayer.setOnTouchListener(new OverlayTouchListener());

3)定义实现View的自定义overlaytouchlistener。现在,这是一个关键的部分,在这个部分中,您必须确定如何处理OnTouchListener,同时避免在MotionEvent方法中返回true而不需要时让它“通过”(这个逻辑取决于您:p)。通过这种方式,您可以避免某些onTouch()消耗事件,因为您在其他人之上!
这里的关键是找出用户正在点击的位置。为此,我发布了以下方法,以检查aView是否截获了具有一定公差的aMotionEvent(它可以在View内部轻松使用):
    /**
     * Useful mathod to detemines if a touchevent can be relevant for the provided view
     * @param ev
     * @param view
     * @param boundingBoxTolerance >= 1 means higher tap-tolerance (bigger bounding-box). Values < 1.0 are handled like 1.0f
     * @return
     */
    private boolean intercept(MotionEvent ev, View view, float boundingBoxTolerance){
        if( view == null )
            return false;

        if(boundingBoxTolerance < 1.0f){
            boundingBoxTolerance = 1.0f;
        }
        try{
            if(ev != null && view != null){
                int coords[] = new int[2];
                view.getLocationOnScreen( coords );
                if(ev.getX() >= ((float)coords[0]) / boundingBoxTolerance  && ev.getX() <= coords[0] + ((float)view.getWidth())*boundingBoxTolerance){
                    if(ev.getY() >= ((float)coords[1]) / boundingBoxTolerance && ev.getY() <= coords[1] +  ((float)view.getHeight())*boundingBoxTolerance)
                        return true;
                }
            }
        }
        catch(Exception e){
            //exception
        }

        return false;
    }

boundingboxtolerance参数很有用,因为您可以通过onTouch()调整特定手势所需的精度。
那么,你该怎么办?您只需根据您的逻辑实现View方法,只要记住在不想让onTouch()通过时返回true。

关于android - 如何将触摸事件从子级重定向到父级布局?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/28111006/

10-11 02:43