安卓按钮OnClick事件偶发失效问题(含SupportMapFragment代码)
看起来你碰到的这个按钮点击时灵时不灵的问题,我之前也遇到过类似的情况——大概率是因为SupportMapFragment抢占了触摸焦点或者在视图层级上意外遮挡了按钮。毕竟你的布局里,地图Fragment和按钮是直接平级放在FrameLayout里的,地图占满了整个屏幕,哪怕你给按钮加了focusable="true",地图控件本身作为一个独立的Fragment,它的内部视图层级很容易干扰按钮的触摸事件传递。
先把你的代码贴出来方便分析:
你的布局代码(fragment_maps.xml)
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.MapsFragment"> <fragment android:id="@+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:id="@+id/hello" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:focusable="true" android:text="Pickup Location"> </Button> </FrameLayout>
你的Fragment代码(MapsFragment.java)
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_maps, container,false); button = (Button) view.findViewById(R.id.hello); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getContext(),"Clicked",Toast.LENGTH_SHORT).show(); } }); location_permissions = new location_permissions(getContext()); locationCallback = new LocationCallback(); locationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE); fusedLocationClient = LocationServices.getFusedLocationProviderClient(getContext()); locationRequest = LocationRequest.create(); locationRequest.setInterval(10000); locationRequest.setFastestInterval(5000); locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() .addLocationRequest(locationRequest); SettingsClient client = LocationServices.getSettingsClient(getContext()); mtask = client.checkLocationSettings(builder.build()); supportMapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map); requestpermission(); // google maps supportMapFragment.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(GoogleMap googleMap) { // request GPS turn on if Gps is off requestGPSCheck(); // gets device current location & animate to current Latlong callback(); } }); return view; }
下面给你几个可以尝试的修复方案,按优先级来:
方案1:提升按钮的触摸优先级
直接给按钮加上android:clickable="true"和android:elevation="8dp"(数值可以根据需要调整),确保按钮在地图视图的上层,优先接收触摸事件:
<Button android:id="@+id/hello" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:focusable="true" android:clickable="true" android:elevation="8dp" android:text="Pickup Location"> </Button>
如果需要兼容Android 5.0以下的版本,可以用ViewCompat.setElevation(button, 8f)在代码里设置,效果是一样的。
方案2:拦截触摸事件,避免地图抢焦点
在onViewCreated方法里给按钮加一个触摸监听器,告诉父布局不要拦截按钮的触摸事件,确保点击能正常触发:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 强制按钮获取焦点 button.requestFocus(); // 拦截触摸事件,不让父布局(地图)抢走 button.setOnTouchListener((v, event) -> { v.getParent().requestDisallowInterceptTouchEvent(true); if (event.getAction() == MotionEvent.ACTION_UP) { v.performClick(); } return true; }); }
这个方法能确保按钮的触摸事件完全由自己处理,不会被地图的手势操作干扰。
方案3:调整布局结构
把FrameLayout换成ConstraintLayout,让按钮和地图的层级关系更明确,避免地图意外遮挡按钮:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.MapsFragment"> <fragment android:id="@+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/hello" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Pickup Location" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
ConstraintLayout会严格按照你设置的约束来排列视图,按钮会始终显示在地图的上层。
方案4:临时禁用地图手势(测试用)
如果上面的方法都不管用,可以先临时禁用地图的拖动手势,看看是不是地图的手势操作和按钮点击冲突了:
@Override public void onMapReady(GoogleMap googleMap) { // 禁用地图拖动手势 googleMap.getUiSettings().setScrollGesturesEnabled(false); // 其他代码... }
如果禁用后按钮点击正常,说明是按钮位置和地图的交互区域重叠了,这时候可以调整按钮的位置,或者在按钮被点击时临时禁用地图手势,点击完成后再恢复。
一般来说,方案1和方案2就能解决大部分问题,你可以先试试这两个。
内容的提问来源于stack exchange,提问作者srujan chandra

