我试图只使用DatePicker
、TextView
和EditText
来实现类似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()
消耗事件,因为您在其他人之上!这里的关键是找出用户正在点击的位置。为此,我发布了以下方法,以检查a
View
是否截获了具有一定公差的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/